鸿蒙OS开发实例:【NAPI入门】

背景

公司内部已经有现成的MQTT动态库,想在HarmonyOS平台上共享使用。查找官方指导后,发现可以通过NAPI方式,将MQTT C++库导入进来,然后封装一层ArkTS接口就可直接使用。

本篇内容是在按照官方指导下,自己做的一些调研性质的实践。

阅读完成后

将学会如何在已有的HarmonyOS应用工程中完成C++文件的添加以及TS与C++的交互

希望大家不用碰到类似的项目需求

NAPI简介

NAPI(Native API)组件是一套对外接口基于Node.js N-API规范开发的原生模块扩展开发框架

在HarmonyOS中,C API中的N-API接口可以实现ArkTS/TS/JS与C/C++之间的交互。N-API提供的接口名与三方Node.js一致,目前支持部分接口。

关于详细HarmonyOS 中的N-API开发教程,详见指导

搜狗高速浏览器截图20240326151547.png

环境条件

  • DevEco Studio 3.1.1 Release
  • HarmonyOS SDK API 9
  • 已更新gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md参考前往。

效果图

功能演示

  1. ets文件获取C++生成的JSON对象
  2. ets文件获取C++生成的数组
  3. 引入第三方源码,并且使用其API
  4. ets获取C++的运算结果
  5. ets实现回调方法, C++触发ets回调方法

Screenshot_20240312155256471.png

实践步骤

备注:以添加 源码 形式演示

简易目录结构

粉色为添加和改动部分

1. entry模块添加文件

  1. src/main 目录下创建“cpp”文件夹【名字必须为cpp】
  2. src/main 目录下创建主入口文件"main.cpp"【名字可以随意命名】
  3. src/main 目录下创建"CMakeLists.txt"文件【内容见 “片段1”】

片段1

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(HarmonyLearn)# 设置源码文件目录
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})# 设置 .h 文件目录
include_directories(${NATIVERENDER_ROOT_PATH}${NATIVERENDER_ROOT_PATH}/include)# 设置自己的动态名称 和 对应的编译文件
add_library(customnapi SHARED main.cppsm4.cpputil.cpp)# 设置链接自己的动态库与其它动态库
target_link_libraries(customnapi PUBLIC libace_napi.z.so)

2. 配置entry模块

entry模块中的build-profile.json5文件增加native选项【内容见 “片段2”】

..."buildOption": {"externalNativeOptions": {"path": "./src/main/cpp/CMakeLists.txt","arguments": "","cppFlags": "",},...
}...

3. ets引用so

在需要使用到"customnapi"库的文件中,通过import方式来进行引用

注意 虽然我们自己定义的库名称为"customnapi", 但是引用的时候应该为"libcustomnapi.so", 注意是以"lib"为前缀

import customnapi from 'libcustomnapi.so'...customnapi.passStr(...)
...

经过这三步,即可完成C++源码在HarmonyOS应用工程中的添加使用

示例解读

main.cpp

这个文件在本示例中用来注册C++源码模块使用的,文件名称可任意修改,因为其最终会被CMakeLists.txt文件引用,所以其文件名称无关紧要

这个文件主要分为四部分

  1. so api注册
  2. so模块说明
  3. napi接口描述
  4. C++函数定义

so注册,即在ets文件被加载时,触发import动作,然后会通过系统机制自动执行 napi_module_register 代码

so模块说明,即对so 模块的描述,包含NAPI接口描述入口,so模块名称,版本等

so api注册, 即ets函数名称和NAPI 函数名称的映射关系。比如,ets中调用add,因为有这里的映射,所以执行的是C++文件中的Add函数。

C++函数定义,即so库对外暴露的接口实现,比如static napi_value Add(napi_env env, napi_callback_info info)

......static napi_value Add(napi_env env, napi_callback_info info) {......
}......// Init将在exports上挂上Add/NativeCallArkTS这些native方法,此处的exports就是开发者import之后获取到的ArkTS对象。static napi_value Init(napi_env env, napi_value exports) {// 函数描述结构体,以Add为例,第三个参数"Add"为上述的native方法,// 第一个参数"add"为ArkTS侧对应方法的名称。napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},......return exports;
}// 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
static napi_module demoModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init,.nm_modname = "customnapi",.nm_priv = ((void *)0),.reserved = {0},
};// 打开so时,该函数将自动被调用,使用上述demoModule模块信息,进行模块注册相关动作。
extern "C" __attribute__((constructor)) void RegisterModule(void) {napi_module_register(&demoModule);
}

C++解析TS方法的参数

例如:Add函数

// 期望从ArkTS侧获取的参数的数量,napi_value可理解为ArkTS value在native方法中的表现形式。
//TS函数有2个参数
size_t argc = 2;//创建一个包含2个元素的TS参数值数组
napi_value args[2] = {nullptr};// 从info中,拿到从ArkTS侧传递过来的参数,此处获取了两个ArkTS参数,即arg[0]和arg[1]。
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 将获取的ArkTS参数转换为native信息,此处ArkTS侧传入了两个number,这里将其转换为native侧可以操作的double类型。//解析TS函数中的第一个参数
double value0;
//声明于 js_native_api.h 文件,属于HarmonyOS SDK里边的一部分
napi_get_value_double(env, args[0], &value0);//解析TS函数中的第二个参数
double value1;
//声明于 js_native_api.h 文件,属于HarmonyOS SDK里边的一部分
napi_get_value_double(env, args[1], &value1);

C++获取TS方法中的字符串类型参数长度

例如:passStr函数

static napi_value passStr(napi_env env, napi_callback_info info) {//TS函数有1一个参数size_t argc = 1;//创建一个包含1个元素的TS参数值数组napi_value args[1] = {nullptr};// 从info中,拿到从ArkTS侧传递过来的参数,此处获取了一个ArkTS参数,即arg[0]。napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);//获取字符串参数的长度size_t typeLen = 0;napi_get_value_string_utf8(env, args[0], nullptr, 0, &typeLen);//创建存储字符串的数组,数组大小为“字符串长度+1”, 1代表字符串结束符char *extContent = new char[typeLen + 1];//解析TS的字符串参数,将内容赋值到char数组中napi_get_value_string_utf8(env, args[0], extContent, typeLen + 1, &typeLen);napi_value result;//将字符串赋值给napi_valuenapi_create_string_utf8(env, extContent, typeLen + 1, &result);//释放数组空间delete[] extContent;//返回结果return result;
}

C++返回数组类型

例如:generatorMockArray函数

static napi_value generatorMockArray(napi_env env, napi_callback_info info) {size_t argc = 1;napi_value args[1] = {nullptr};napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);//解析TS参数值,这里的值代表需要初始化的数组容量int result;napi_get_value_int32(env, args[0], &result);napi_value ret;// 创建返回类型为数组的对象napi_create_array(env, &ret);const int arrayLength = 4;const char *target[arrayLength] = {"Blue", "Red", "Orange", "Yellow"};// 根据TS入参设置数组的初始值for (int i = 0; i < result && i < arrayLength; i++) {napi_value tempValue;//创建字符串类型的tempValue对象,把字符串赋值给tempValuenapi_create_string_utf8(env, target[i], strlen(target[i]),&tempValue);// 数组中添加元素napi_set_element(env, ret, i, tempValue);}return ret;
}

C++返回Object类型对象

例如:getCustomObject函数

/*** 获取mock对象* * 格式* {*   "testNumber" : 123,*   "testString" : "welcome",*   "testNested" : { "child" : "welcome"}* }* * @param env* @param info* @return */
static napi_value getCustomObject(napi_env env, napi_callback_info info) {//创建一个数字类型的对象: value_numbernapi_value value_number;napi_create_int32(env, 123, &value_number);//创建一个字符串类型的对象: value_stringchar *contentStr = "welcome";napi_value value_string;napi_create_string_utf8(env, contentStr, strlen(contentStr), &value_string);//创建一个Object类型的对象:childObjnapi_value childObj;napi_create_object(env, &childObj);//childObj对象添加一个名为“child”的属性,其值为value_stringnapi_set_named_property(env, childObj, "child", value_string);//创建一个返回类型为Object的对象: objnapi_value obj;napi_create_object(env, &obj);//obj对象添加一个名为“testNumber”的属性,其值为value_numbernapi_set_named_property(env, obj, "testNumber", value_number);//obj对象添加一个名为“testString”的属性,其值为value_stringnapi_set_named_property(env, obj, "testString", value_string);//obj对象添加一个名为“testNested”的属性,其值为childObjnapi_set_named_property(env, obj, "testNested", childObj);return obj;
}

CMakeLists.txt

# cmake 最小版本
cmake_minimum_required(VERSION 3.4.1)
# 工程名称:HarmonyLearn
project(HarmonyLearn)# set命令,格式为set(key value),表示设置key的值为value,其中value可以是路径,也可以是许多文件。
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})# 添加项目编译所需要的头文件的目录
include_directories(${NATIVERENDER_ROOT_PATH}${NATIVERENDER_ROOT_PATH}/include)# 生成目标库文件customnapi.so,customnapi表示最终的库名称,SHARED表示生成的是动态链接库,
# main.cpp, sm4.cpp, util.cpp 表示最终生成的libcustomnapi.so中所包含的源码
# 如果要生成静态链接库,把SHARED该成STATIC即可
add_library(customnapi SHARED main.cppsm4.cpputil.cpp)
# 把libcustomnapi.so链接到libace_napi.z.so上
target_link_libraries(customnapi PUBLIC libace_napi.z.so)

结尾

如果平时不太写C/C++的情况下,上手写NAPI,还是比较困难的,相当于HarmonyOS新手不知道如何展示一个文字一样。

最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

高清完整版请点击→《鸿蒙NEXT星河版开发学习文档》

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

《鸿蒙 (OpenHarmony)开发学习视频》

图片

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

OpenHarmony北向、南向开发环境搭建

图片

《鸿蒙开发基础》

  1. ArkTS语言

  2. 安装DevEco Studio

  3. 运用你的第一个ArkTS应用

  4. ArkUI声明式UI开发

  5. .……

图片

《鸿蒙开发进阶》

  1. Stage模型入门

  2. 网络管理

  3. 数据管理

  4. 电话服务

  5. 分布式应用开发

  6. 通知与窗口管理

  7. 多媒体技术

  8. 安全技能

  9. 任务管理

  10. WebGL

  11. 国际化开发

  12. 应用测试

  13. DFX面向未来设计

  14. 鸿蒙系统移植和裁剪定制

  15. ……

图片

《鸿蒙开发实战》

  1. ArkTS实践

  2. UIAbility应用

  3. 网络案例

  4. ……

图片

 获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/786056.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Latex绘制多行多TSNE列子图

Latex绘制多行多列TSNE子图 问题描述解决办法 问题描述 写论文需要绘制TSNE可视化图像。 解决办法 代码如下 \usepackage{subfigure}\begin{figure*}\centering\small\subfigure[aaa]{\includegraphics[width0.18\textwidth]{Figure/MFPT_v5_train_tsne_user0_bs0.png}}\su…

Docker 学习

一起学习,加油芹菜!!! 镜像:相当于模板,容器:相当于服务器 镜像:(centos镜像、mysql镜像、nginx镜像、redis镜像、自己的应用程序等) 容器:想让镜像跑起来就需要创建容器,相当于服务器,容器与容器之间是隔离的,安全性较高 1)yum包更新到最新 sudo yum upd…

neo4j使用详解(六、cypher即时时间函数语法——最全参考)

Neo4j系列导航&#xff1a; neo4j及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 6.时间函数-即时类型 表示具体的时刻的时间类型函数 6.1.date函数 年-月-日时间函数&#xff1a; yyyy-mm-dd 6.1.1.获取date da…

C++ vector 数组/向量

文章目录 【 1. vector 的声明与初始化 】1.1 vector 的声明1.2 vector 的初始化1.2.1 构造一个空的 vector1.2.2 指定数量初值的方式初始化 vector1.2.3 迭代器的方式初始化1.2.4 构造一个相同的 vector 【 2. vector 的相关操作 】2.1 插入元素2.1.1 在vector的末尾插入新元素…

IPv4地址

IP v4 由32位二进制构成、可以用点分十进制表示。 例如&#xff1a;192.168.1.1 11000000101010000000000100000001 由网络位和主机位组成。为了区分网络位和主机位&#xff0c;需要用子网掩码&#xff0c;子网掩码也是由32位二进制构成&#xff0c;连续的1对应网络位&#…

HDMI 2.1b 规范解读

HDMI 规范 HDMI 2.1b 是最新版 HDMI 规范&#xff0c;支持一系列更高的视频分辨率和刷新频率&#xff0c;包括 8K60 和 4K120 以及高达 10K 的分辨率。同时支持动态 HDR 格式&#xff0c;带宽能力增加到 48Gbps HDMI。 新的超高速 HDMI 线缆支持 48Gbps 带宽。该线缆可确保提供…

吴恩达机器学习笔记 三十二 K-means初始化 选择聚类数量

K-means 初始化 首先 K < m&#xff0c;若集群的数量多于样本数显然是不合理的。 第一步&#xff1a;随机选取 K 个样本&#xff1b; 第二步&#xff1a;设这 K 个样本为 μ1 μ2 ... μk 有时候K-meas 算法会出现局部最小值的情况&#xff0c;如左下图和右下图&#xf…

spark sql执行引擎原理及配置

如果我们想要给上层开发人员配置好一个统一的sql开发界面&#xff0c;让他们统一通过sql开发即可&#xff0c;可通过spark中的thriftserver服务实现&#xff0c;与hive中的thriftserver类似&#xff0c;配置好该服务后&#xff0c;上层通过db client或者代码中通过jdbc连接即可…

Linux gcc day2

mkdir -p 递归的创建目录 rm or rmdir&#xff1a; rmdir &#xff1a;是用来删除空目录的 实际上我们更加常用的是rm命令 rm可以删除普通文件,也可以删除目录&#xff0c;目录是从某次开始就是一棵树就是递归&#xff0c;所以就要递归删除 rm -r [文件名] 递归删除目录或者目…

linux系统命令chkconfig详解,管理系统服务的工具-查看、启用、禁用和设置系统服务的启动级别

目录 一、chkconfig命令介绍 二、命令的主要作用 1、管理服务的启动和停止&#xff1a; 2、配置运行级别&#xff1a; 3、简化系统管理&#xff1a; 4、查看服务状态&#xff1a; 三、命令语法 1、基本语法 2、运行级别 四、获取帮助 1、通过help获取 2、通过man获…

基于“遥感+”蓝碳储量估算、红树林信息提取实践技术应用与科研论文写作

原文链接&#xff1a;基于“遥感”蓝碳储量估算、红树林信息提取实践技术应用与科研论文写作https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247599633&idx5&sn1398cde523f9c5e036e4d478fc1d3388&chksmfa8207f6cdf58ee0a3a2bf652e7ac5cf65636b05f9e3306…

备战蓝桥杯Day36 - 动态规划 - 三角形最小路径和问题

一、什么是动态规划 通过拆分问题&#xff0c;定义问题状态和状态之间的关系&#xff0c;使得问题能够以递推的方式解决。 哪些问题可以使用动态规划&#xff1f; 1、具有最优子结构&#xff1a;问题的最优解所包含的子结构的解也是最优的 2、具有无后效性&#xff1a;未来…

java项目依赖的依赖加载不到

1、描述&#xff1a; 一个项目包含多个子模块&#xff0c;并且可以根据profiles切换环境。 然后依赖的依赖就是加载不到 上面flow-es-6.8是我封装的一个环境。 2、解决: profiles里要加上默认一个环境配置,如下红色部分&#xff1a; 虽然我在idea右边maven中的profiles选项…

【ElementUI】详细分析DatePicker 日期选择器

目录 前言1. 通用Demo2. 快捷键3. 参数 前言 对于全栈玩家&#xff0c;各个组件都需相应了解才可做好前后端的CRUD 以下为实战结合Element官网的心得体会 如图所示&#xff1a; 1. 通用Demo 如果只想要一个选择日期&#xff0c;而不是范围&#xff0c;Demo如下&#xff1a;…

Docker搭建LNMP环境实战(09):安装mariadb

1、编写mariadb部署配置文件 在文件夹&#xff1a;/mnt/hgfs/dockers/test_site/compose下创建文件&#xff1a;test_site_mariadb.yml&#xff0c;内容如下&#xff1a; version: "3.5" services:test_site_mariadb:container_name: test_site_mariadbimage: mari…

逐步学习Go-Select多路复用

概述 这里又有多路复用&#xff0c;但是Go中的这个多路复用不同于网络中的多路复用。在Go里&#xff0c;select用于同时等待多个通信操作&#xff08;即多个channel的发送或接收操作&#xff09;。Go中的channel可以参考我的文章&#xff1a;逐步学习Go-并发通道chan(channel)…

Gparted工具 初始化磁盘

Gparted工具 初始化磁盘 1、安装 没有此工具请先安装&#xff1a; yum install epel-release yum install gparted yum install yum-utils git gnome-common gcc-c yum-builddep gparted 2、打开Gparted工具&#xff0c;初始化磁盘 使用具有root权限的普通用户打开gparted&…

HWOD:整型数组排序

一、知识点 while(1){}表示永久循环 使用break结束循环 二、题目 1、描述 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序 2、数据范围 1<n<1000 0<val<100000 3、输入 第一行输入数组元素个数 第二行输入待排序的数组&#x…

电商技术揭秘二:电商平台推荐系统的实现与优化

文章目录 一、推荐系统的重要性1.1 提升用户体验1.1.1 个性化推荐增强用户满意度1.1.2 减少用户选择困难 1.2 增加销售额1.2.1 促进交叉销售和捆绑销售1.2.2 提高用户购买转化率 1.3 数据分析与用户行为理解1.3.1 挖掘用户偏好和购买习惯1.3.2 为产品开发和库存管理提供数据支持…

npm ERR! code CERT_HAS_EXPIRED 淘宝镜像失效

近期vue安装失败&#xff0c;具体如下&#xff1a; 1.先npm cache clean --force 再下载 插件后缀加上 --legacy-peer-deps 2.certificate has expired npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.o…