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

news2024/11/24 12:37:17

背景

公司内部已经有现成的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 万款的应用那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

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

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

相关文章

面对复杂多变的网络攻击,企业应如何守护网络安全

企业上云&#xff0c;即越来越多的企业把业务和数据&#xff0c;迁移到云端。随着云计算、大数据、物联网、人工智能等技术的发展&#xff0c;用户、应用程序和数据无处不在&#xff0c;企业之间的业务边界逐渐被打破&#xff0c;网络攻击愈演愈烈&#xff0c;手段更为多。 当前…

目标检测:数据集划分 XML数据集转YOLO标签

文章目录 1、前言&#xff1a;2、生成对应的类名3、xml转为yolo的label形式4、优化代码5、划分数据集6、画目录树7、目标检测系列文章 1、前言&#xff1a; 本文演示如何划分数据集&#xff0c;以及将VOC标注的xml数据转为YOLO标注的txt格式&#xff0c;且生成classes的txt文件…

AI绘图cuda与stable diffusion安装部署始末与避坑

stable diffusion的安装说起来很讽刺&#xff0c;最难的不是stable diffusion&#xff0c;而是下载安装cuda。下来我就来分享一下我的安装过程&#xff0c;失败了好几次&#xff0c;几近放弃。 一、安装cuda 我们都知道cuda是显卡CPU工作的驱动&#xff08;或者安装官网的解释…

vscode前后台分离Nodejs+vue校园影院售票系统_490gq

柚子校园影院在设计与实施时&#xff0c;采取了模块性的设计理念&#xff0c;把相似的系统的功能整合到一个模组中&#xff0c;以增强内部的功能&#xff0c;减少各组件之间的联系&#xff0c;从而达到减少相互影响的目的。 后台主要包括首页&#xff0c;个人中心&#xff0c;用…

【QT+QGIS跨平台编译】056:【pdal_arbiter+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

点击查看专栏目录 文章目录 一、pdal_arbiter介绍二、pdal下载三、文件分析四、pro文件五、编译实践一、pdal_arbiter介绍 pdal_arbiter是 PDAL 项目的一个库,用于帮助管理应用程序运行在 EC2 实例上的 AWS 凭证。 当应用程序需要调用 AWS API 时,它们必须使用 AWS 凭据对 AP…

Git、TortoiseGit、SVN、TortoiseSVN 的关系和区别

Git、TortoiseGit、SVN、TortoiseSVN 的关系和区别 &#xff08;二&#xff09;Git&#xff08;分布式版本控制系统&#xff09;:&#xff08;二&#xff09;SVN&#xff08;集中式版本控制系统&#xff09;&#xff08;三&#xff09;TortoiseGit一、下载安装 git二、安装过程…

HarmonyOS 应用开发之通过关系型数据库实现数据持久化

场景介绍 关系型数据库基于SQLite组件&#xff0c;适用于存储包含复杂关系数据的场景&#xff0c;比如一个班级的学生信息&#xff0c;需要包括姓名、学号、各科成绩等&#xff0c;又或者公司的雇员信息&#xff0c;需要包括姓名、工号、职位等&#xff0c;由于数据之间有较强…

docker容器之etcd

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

HBuilder uniapp发行h5遇到报错:此应用 DCloud appid 为 __UNI__95950AD ,您不是这个应用的项目成员。

uniapp打包遇到不是项目成员问题&#xff0c;如下截图&#xff1a; 解决方法如下&#xff1a; 打开项目的mainfest.json文件&#xff0c;在AppID位置点击重新获取&#xff0c;获取后重新点发行打包即可 另遇到HBuilder账号认证问题&#xff0c;如公司wifi打不开认证地址&#…

深入理解 SQL 中的数据集合和数据关联

引言 在数据库管理系统中&#xff0c;数据集合和数据关联是 SQL 查询中常见的概念。它们是构建复杂查询和分析数据的基石。本文将深入探讨 SQL 中的数据集合和数据关联&#xff0c;包括它们的概念、常见用途以及实际示例。 首先引入一下数学中的集合 集合的基本概念&#x…

【MATLAB源码-第26期】基于matlab的FBMC/OQAM的误码率仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 FBMC&#xff08;Filter Bank Multicarrier&#xff09;是一种多载波调制技术&#xff0c;它采用滤波器组来处理频域内的子载波&#xff0c;以在有限带宽内实现高效的数据传输。OQAM&#xff08;Offset Quadrature Amplitude…

OpenHarmony分布式五子棋-使用Canvas组件 实现棋盘、棋子的绘制

介绍 五子棋是一款比较流行的棋类游戏&#xff0c;此游戏使用分布式数据管理功能开发完成的。 本示例使用Canvas组件 实现棋盘、棋子的绘制&#xff0c;使用分布式数据管理 实现两台设备间数据的同步。 本示例使用分布式设备管理能力接口ohos.distributedDeviceManager。 分…

【stm32】USART编码部分--详细步骤

USART编码部分(文章最后附上源码) 如果看不懂步骤可以根据源码参考此篇文章就能轻而易举学会USART通信啦&#xff01; 编码步骤 第一步 开启时钟 把需要用到的USART和GPIO的时钟打开 第二部 GPIO初始化 把TX配置成复用输出&#xff0c;RX配置成输入(上拉输入、浮空输入)。…

VMware虚拟机添加磁盘

在VMware中添加磁盘 &#xff08;虚拟机关闭状态下执行&#xff09; 然后选择默认一步一步点下去&#xff0c;最后创建好新磁盘 开启虚拟机&#xff0c;挂载磁盘 通过命令 lsblk -f 查看未挂载的新磁盘 lsblk -f 通过fdisk命令进行磁盘分区 # 1. 给硬盘/dev/sdb进行分区&am…

2024年04月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2024年04月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

Qt6.6添加多媒体模块Multimedia报错问题

问题 QT包含多媒体模块Multimedia时提示未知的模块&#xff1a; error: Project ERROR: Unknown module(s) in QT: multimedia 在帮助文档中只可以找到QMediaPlayer类&#xff0c;但是点进去是空的&#xff0c;这是因为没有安装多媒体模块及对应的帮助文档。 解决 使用在线…

RTOS中临界区嵌套保护的实现原理(基于RT-Thread)

0 前言 什么是临界区&#xff08;临界段&#xff09;&#xff1f; 裸机编程中由于不涉及线程和线程切换&#xff0c;因此没有临界区这一个概念。在RTOS中由于存在线程切换等场景&#xff0c;便有了临界区这个概念。简单来说&#xff0c;临界区就是不允许被中断的代码区域。什么…

【操作系统】FCFS、SJF、HRRN、RR、EDF、LLF调度算法及python实现代码

文章目录 一、先来先服务调度算法&#xff08;FCFS&#xff09; 二、短作业优先调度算法&#xff08;SJF&#xff09; 三、高响应比优先调度算法&#xff08;HRRN&#xff09; 四、轮转调度算法&#xff08;RR&#xff09; 五、最早截至时间优先算法&#xff08;EDF&#…

ES学习日记(七)-------Kibana安装和简易使用

前言 首先明确一点&#xff0c;Kibana是一个软件&#xff0c;不是插件。 Kibana 是一款开源的数据分析和可视化平台&#xff0c;它是 Elastic stack 成员之一&#xff0c;设计用于和Elasticsearch 协作。您可以使用 Kibana 对 Elasticsearch 索引中的数据进行搜索&#xff0c;…

机器学习——卷积的变种

机器学习——卷积的变种 卷积神经网络&#xff08;Convolutional Neural Networks, CNNs&#xff09;是深度学习领域中最重要的技术之一&#xff0c;它在图像处理、语音识别、自然语言处理等领域取得了巨大成功。在CNN中&#xff0c;卷积层是最核心的组成部分之一&#xff0c;…