鸿蒙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.cpp
                         sm4.cpp
                         util.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_value
    napi_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对象,把字符串赋值给tempValue
        napi_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_number
    napi_value value_number;
    napi_create_int32(env, 123, &value_number);

    //创建一个字符串类型的对象: value_string
    char *contentStr = "welcome";

    napi_value value_string;
    napi_create_string_utf8(env, contentStr, strlen(contentStr), &value_string);

    //创建一个Object类型的对象:childObj
    napi_value childObj;
    napi_create_object(env, &childObj);
    //childObj对象添加一个名为“child”的属性,其值为value_string
    napi_set_named_property(env, childObj, "child", value_string);
    
    //创建一个返回类型为Object的对象: obj
    napi_value obj;
    napi_create_object(env, &obj);
    
    //obj对象添加一个名为“testNumber”的属性,其值为value_number
    napi_set_named_property(env, obj, "testNumber", value_number);
    
    //obj对象添加一个名为“testString”的属性,其值为value_string
    napi_set_named_property(env, obj, "testString", value_string);
    
    //obj对象添加一个名为“testNested”的属性,其值为childObj
    napi_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.cpp
                         sm4.cpp
                         util.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 万款的应用那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ARMv8-A架构下的外部debug模型(external debug)简介

Armv8-A external debug Armv8-A debug模型一&#xff0c;外部调试 External debug 简介二&#xff0c;Debug state2.1 Debug state的进入与退出 三&#xff0c;DAP&#xff0c;Debug Access Port3.1 EDSCR, External Debug Status and Control Register调试状态标识&#xff0…

自动驾驶---Motion Planning之轨迹Speed优化

1 背景 在之前的几篇文章中&#xff0c;不管是通过构建SL图《自动驾驶---Motion Planning之Path Boundary》&#xff0c;ST图《自动驾驶---Motion Planning之Speed Boundary》&#xff0c;又或者是构建SLT图《自动驾驶---Motion Planning之构建SLT Driving Corridor》&#xff…

【opencv】教程代码 —features2D(7)根据单应性矩阵估计相机坐标系下的物体位姿...

pose_from_homography.cpp从图像中找到棋盘角点并进行姿态估计 从图像中找到棋盘角点并显示 计算角点在世界坐标系中的位置 读取相机内参和畸变系数并校正图像中的角点 计算从3D点到2D点的单应性矩阵 通过奇异值分解(SVD)优化对旋转矩阵的估计 基于单应矩阵分解及其优化结果&am…

【数据结构】非线性结构---二叉树

1、树 1.1 树的相关概念 节点的度&#xff1a;一个节点含有的子树的个数称为该节点的度&#xff1b; 如上图&#xff1a;A的为6 叶节点或终端节点&#xff1a;度为0的节点称为叶节点&#xff1b; 如上图&#xff1a;B、C、H、I...等节点为叶节点 非终端节点或分支节点&#…

前端之CSS——网页的皮肤!!

目录 一、CSS简单介绍 二、css内容 2.1 css的编写方式 2.2 css选择器 2.3 样式属性 2.4 css包围盒 2.5 css中的display 2.6 css中的定位 2.7 css中的浮动与清除 2.7 弹性容器 2.8 字体图标 2.9 …

单片机简介(一)

51单片机 一台能够运行的计算机需要CPU做运算和控制&#xff0c;RAM做数据存储&#xff0c;ROM做程序存储&#xff0c;还有输入/输出设备&#xff08;串行口、并行输出口等&#xff09;&#xff0c;这些被分为若干块芯片&#xff0c;安装在主板&#xff08;印刷线路板&#xf…

探索组合总和问题(力扣39,40,216)

文章目录 题目前知LinkedList和ArryayList 组合总和I一、思路二、解题方法三、Code 组合总和II一、思路二、解题方法三、Code 组合总和III一、思路二、解题方法三、Code 总结 先看完上一期组合问题再看这一期更加容易理解喔&#x1f92f; 在算法和编程的世界中&#xff0c;组合…

文本直接生成2分钟视频,即将开源模型StreamingT2V

Picsart人工智能研究所、德克萨斯大学和SHI实验室的研究人员联合推出了StreamingT2V视频模型。通过文本就能直接生成2分钟、1分钟等不同时间&#xff0c;动作一致、连贯、没有卡顿的高质量视频。 虽然StreamingT2V在视频质量、多元化等还无法与Sora媲美&#xff0c;但在高速运…

6款Mac垃圾清理软件横评 Mac电脑清理软件哪个好 cleanmymac评测

鉴于苹果笔记本昂贵的硬盘价格&#xff0c;导致我们不得不定期清理自己的硬盘空间&#xff0c;释放给真正有用的各种程序等。 即便我们把程序安装到外置硬盘&#xff0c;但是程序运行时的缓存&#xff0c;仍然是在内置的硬盘中。 今天就让我们对比看看&#xff0c;目前市面上…

华为数通方向HCIP-DataCom H12-821题库(多选题:241-260)

第241题 [RTAospf100 [RTA-ospf-100]silent-intefaceGigabitEthernet 1/0/0上面是路由器RTA的部分配置,对于此部分的配置描述,正确的是: A、接口gigabitethemet 1/0/0的直连路由仍然可以发布出去 B、无法与该接口的直连邻居形成邻居关系 C、禁止接口gigabi tethemet 1/0/0发…

JavaEE初阶-线程2

文章目录 一、多线程安全问题1.1 线程安全问题的原因1.2 如何解决线程安全问题 二、加锁2.1 synchronized2.2 synchronized的几种使用方式2.3 synchronized的可重入性 三、死锁3.1 死锁的必要条件 一、多线程安全问题 代码示例如下&#xff1a; public class Demo20 {static …

直流电源电路(上)

直流电源电路&#xff08;上&#xff09; 综述&#xff1a;本篇文章讲述了直流电源电路的各种类型以及他们之间的优缺点对比。 一、总体关系框图 二、LDO 1&#xff09;LDO基础知识 2&#xff09;LDO电路框图 LDO电路由调整管、误差放大器、基准电压和采样电路组成。 3&…

docker容器之etcd

一、etcd介绍 1、etcd是什么 etcd是CoreOS团队于2013年6月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。 2、etcd特点 简单的接口&#xff0c;通过标准的HTTP API进行调用&#xff0c;也可以使用官方提供的 etcdctl 操作存储的数据。…

【战略前沿】与中国达成生产协议后,飞行汽车即将起飞

【原文】Flying cars edge towards takeoff after Chinese production deal 【作者】Thomas Macaulay 斯洛伐克公司KleinVision签署了一项协议&#xff0c;将大规模生产AirCar。 一辆获得航空认证的飞行汽车向商业化又迈出了一大步。 空中汽车的创造者KleinVision今天宣布出售…

Anaconda/Python快速安装jieba 【win/mac】

一、直接上命令 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jieba 我实在PyCharm里面的终端输进去。 之后就很快速的看到成功的下图。 二、官网 官网下载的速度太慢了——这是官网地址https://pypi.org/project/jieba/#files 点进去之后点击下载&#xff0c…

黑马鸿蒙笔记 3

目录 11.ArkUI组件-Column和Row 12.ArkUI组件-循环控制 13.ArkUI组件-List 14.ArkUI组件-自定义组件 15.ArkUI组件-状态管理State装饰器 16.ArkUI组件-状态管理-任务统计案例 17.ArkUI组件-状态管理-PropLinkProvideConsume 11.ArkUI组件-Column和Row Colum和Row的交叉…

Docker容器与Serverless的融合:探索《2023腾讯云容器和函数计算技术实践精选集》中的云原生创新案例

Docker容器与Serverless的融合&#xff1a;探索《2023腾讯云容器和函数计算技术实践精选集》中的云原生创新案例 文章目录 Docker容器与Serverless的融合&#xff1a;探索《2023腾讯云容器和函数计算技术实践精选集》中的云原生创新案例一、引言二、《2023腾讯云容器和函数计算…

Tailscale:随时随地远程和使用服务器

文章目录 Tailscale是什么&#xff1f;Tailscale能做什么&#xff1f;1、传输文件2、远程开发3、代理 Tailscale怎么用&#xff1f;Windows下安装OpenSSH在线安装离线安装连接SSH服务器 Reference相关阅读 彩蛋&#xff1a;Pycharm远程连接服务器并运行代码 Tailscale是什么&am…

3d怎么两个模型连接圆润?---模大狮模型网

在3D建模中&#xff0c;如何实现两个3d模型的圆润连接是一个常见而又关键的问题。无论是为了美观的外观设计还是为了模型的功能性&#xff0c;圆润连接都能够增加模型的整体质感和流畅度。模大狮将介绍一些常见的方法和技巧&#xff0c;帮助您实现两个模型之间的圆润连接。 一、…

maven构建项目报错:Failure to find com.microsoft.sqlserver:sqljdbc4:jar:4.0 in

背景 今天在项目里面查询sqlserver的数据库的时候&#xff0c;本地maven中引入依赖&#xff1a; <dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>sqljdbc4</artifactId><version>4.0</version></dependenc…