鸿蒙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,一经查实,立即删除!

相关文章

微信小程序开发:颠覆式思维转变与实践指南

在小程序开发中,我们需要转变展现形式、开发模式、体验思路、发布模式、性能思路和技术选型等多方面的思维。以下通过一些代码示例,展示微信小程序开发中需要转变的思路。 展现形式转变 在传统Web开发中,我们通常这样切换页面: <!-- index.html --> <a href"a…

Leetcode 16. 最接近的三数之和

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数&#xff0c;使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 示例 1&#xff1a; 输入&#xff1a;nums [-1,2,1,-4], target 1 输出&#xff1a;…

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…

Windows下配深度学习环境

Requirements Python version 3.8.5Pytorch version: pytorch1.6.0 torchvision0.8.2 torchaudio0.7.0 cudatoolkit10.2.89pytorch3d version 0.3.0Cuda version 10.2 感觉readme文件里的不适配&#xff0c;怎么办

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的末尾插入新元素…

Linux 网络: 网卡速度异常案例(1)

文章目录 1. 前言2. 问题描述3. 问题简析 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 问题描述 在 TI 的 AM335x 平台适配了一个 1000Mbps 的 PHY 芯片&#xff0c;通过 iperf 打流&#x…

C语言最详细内存函数

1. memcpy 使用和模拟实现 void * memcpy(void *destination,const void * source,size_t num);• 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。 • 这个函数在遇到 ‘\0’ 的时候并不会停下来。 • 如果source和destination有任何的重…

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 带宽。该线缆可确保提供…

TS学习02 面向对象 类、封装继承、接口、泛型

TS学习02 TypeScript 面向对象1、类&#xff08;class&#xff09;属性实例属性&类属性只读属性 方法实例方法类方法 构造函数 & this 2、面向对象的特点封装属性的权限属性存取器静态属性 继承重写super抽象类 3、接口&#xff08;Interface&#xff09;4、泛型&#…

吴恩达机器学习笔记 三十二 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连接即可…

Android U user+root实现方案

背景 由于项目&#xff08;MTK平台&#xff09;上要实现userroot的版本&#xff0c;供特殊用户使用。Android T上的方案无效&#xff0c;经历了各种搜索查看资料&#xff0c;和bsp大佬一起通宵奋战&#xff0c;整出了方案。梳理记录下&#xff0c;有需要的同学可以参考。 Root…

cesium键盘控制相机位置和姿态

该类主要用于监听键盘事件并在用户按下不同按键时执行相应的相机操作&#xff0c;如改变相机的位置、偏航角、俯仰角和翻滚角&#xff0c;从而实现在三维场景中的漫游。 以下是代码的主要逻辑&#xff1a; 导入Cesium库&#xff0c;并定义一个flags对象&#xff0c;其中包含了…

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获…

Azure入门实践-如何创建两个虚拟网络的对等连接

在Azure中创建两个虚拟网络&#xff08;Virtual Network, VNet&#xff09;之间的对等连接&#xff08;VNet Peering&#xff09;可以让这两个虚拟网络中的资源像在同一个网络中一样相互通信。以下是详细的步骤&#xff1a; 1. 登录到Azure门户 打开浏览器&#xff0c;访问Az…

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

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