【鸿蒙HarmonyOS Next实战开发】实现ArkTS/JS和C/C++的交互-Node-API

一、HarmonyOS Node-API简介

在HarmonyOS应用开发中,通常以ArkTS/JS语言为主,但在一些特殊场景下,例如游戏开发、物理模拟等,由于对性能、效率等有较高要求,需要借助现有的C/C++库来实现。为了满足这种需求,HarmonyOS引入了Node-API规范,通过封装I/O、CPU密集型、OS底层等能力,并对外暴露ArkTS/JS接口,从而实现了ArkTS/JS与C/C++之间的高效交互。

HarmonyOS Node-API是基于Node.js 12.x LTS的Node-API规范进行扩展开发的一种机制,它为开发者提供了ArkTS/JS与C/C++模块之间的交互能力。该机制提供了一组稳定且跨平台的API,能够在不同的操作系统上实现无缝使用。

在本文中,若无特别说明,后续提到的“Node-API”均指代HarmonyOS Node-API的相关能力。

关于HarmonyOS Node-API与Node.js 12.x LTS的Node-API规范在接口方面的异同点,可见Node-API参考。

其主要的应用场景包括但不限于以下几种:

  • 系统功能开放:系统能够通过ArkTS/JS接口,将框架层丰富的模块功能开放给上层应用,方便开发者调用系统底层能力,实现更强大的功能扩展。

  • 性能优化与核心功能封装:应用开发者可以将对性能要求较高或需要底层系统调用的核心功能,使用C/C++进行封装实现,然后通过ArkTS/JS接口在应用中调用,从而显著提高应用的执行效率。

1.Node-API的组成架构

图1 Node-API的组成架构

  • Native Module:开发者使用Node-API开发的模块,用于在ArkTS侧导入使用。

  • Node-API:实现ArkTS与C/C++交互的逻辑。

  • ModuleManager:Native模块管理,包括加载、查找等。

  • ScopeManager:管理napi_value的生命周期。

  • ReferenceManager:管理napi_ref的生命周期。

  • NativeEngine:ArkTS引擎抽象层,统一ArkTS引擎在Node-API层的接口行为。

  • ArkCompiler ArkTS Runtime:ArkTS运行时。

2.Node-API的关键交互流程

图2 Node-API的关键交互流程

ArkTS和C++之间的交互流程,主要分为以下两步:

  1. 初始化阶段:当ArkTS侧在import一个Native模块时,ArkTS引擎会调用ModuleManager加载模块对应的so及其依赖。首次加载时会触发模块的注册,将模块定义的方法属性挂载到exports对象上并返回该对象。

  2. 调用阶段:当ArkTS侧通过上述import返回的对象调用方法时,ArkTS引擎会找到并调用对应的C/C++方法。

二、使用Node-API实现跨语言交互开发流程

使用Node-API实现跨语言交互,首先需要按照Node-API的机制实现模块的注册和加载等相关动作。

  • ArkTS/JS侧:实现C++方法的调用。代码比较简单,import一个对应的so库后,即可调用C++方法。

  • Native侧:.cpp文件,实现模块的注册。需要提供注册lib库的名称,并在注册回调方法中定义接口的映射关系,即Native方法及对应的JS/ArkTS接口名称等。

此处以在ArkTS/JS侧实现add()接口、在Native侧实现Add()接口,从而实现跨语言交互为例,呈现使用Node-API进行跨语言交互的流程。

1.创建Native C++工程

  • 在DevEco Studio中New > Create Project,选择Native C++模板,点击Next,选择API版本,设置好工程名称,点击Finish,创建得到新工程。

  • 创建工程后工程结构可以分两部分,cpp部分和ets部分,工程结构具体介绍可见C++工程目录结构。

2.Native侧方法的实现

  • 设置模块注册信息

    ArkTS侧import native模块时,会加载其对应的so。加载so时,首先会调用napi_module_register方法,将模块注册到系统中,并调用模块初始化函数。

    napi_module有两个关键属性:一个是.nm_register_func,定义模块初始化函数;另一个是.nm_modname,定义模块的名称,也就是ArkTS侧引入的so库的名称,模块系统会根据此名称来区分不同的so。

// entry/src/main/cpp/napi_init.cpp// 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = nullptr,
.reserved = {0},
};// 加载so时,该函数会自动被调用,将上述demoModule模块注册到系统中。
extern "C" __attribute__((constructor)) void RegisterDemoModule() {
napi_module_register(&demoModule);
}

注:以上代码无须复制,创建Native C++工程以后在napi_init.cpp代码中已配置好。

  • 模块初始化

    实现ArkTS接口与C++接口的绑定和映射。

// entry/src/main/cpp/napi_init.cpp
EXTERN_C_START
// 模块初始化
static napi_value Init(napi_env env, napi_value exports) {
// ArkTS接口与C++接口的绑定和映射
napi_property_descriptor desc[] = {
// 注:仅需复制以下两行代码,Init在完成创建Native C++工程以后在napi_init.cpp中已配置好。
{"callNative", nullptr, CallNative, nullptr, nullptr, nullptr, napi_default, nullptr},
{"nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr}
};
// 在exports对象上挂载CallNative/NativeCallArkTS两个Native方法
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
  • 在index.d.ts文件中,提供JS侧的接口方法。

// entry/src/main/cpp/types/libentry/index.d.ts
export const callNative: (a: number, b: number) => number;
export const nativeCallArkTS: (cb: (a: number) => number) => number;
  • 在oh-package.json5文件中将index.d.ts与cpp文件关联起来。

// entry/src/main/cpp/types/libentry/oh-package.json5
{
"name": "libentry.so",
"types": "./index.d.ts",
"version": "",
"description": "Please describe the basic information."
}
  • 在CMakeLists.txt文件中配置CMake打包参数。

# entry/src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(MyApplication2)set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)# 添加名为entry的库
add_library(entry SHARED napi_init.cpp)
# 构建此可执行文件需要链接的库
target_link_libraries(entry PUBLIC libace_napi.z.so)
  • 实现Native侧的CallNative以及NativeCallArkTS接口。具体代码如下:

// entry/src/main/cpp/napi_init.cpp
static napi_value CallNative(napi_env env, napi_callback_info info)
{
size_t argc = 2;
// 声明参数数组
napi_value args[2] = {nullptr};// 获取传入的参数并依次放入参数数组中
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 依次获取参数
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);// 返回两数相加的结果
napi_value sum;
napi_create_double(env, value0 + value1, &sum);
return sum;
}static napi_value NativeCallArkTS(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);// 创建一个int,作为ArkTS的入参
napi_value argv = nullptr;
napi_create_int32(env, 2, &argv );// 调用传入的callback,并将其结果返回
napi_value result = nullptr;
napi_call_function(env, nullptr, args[0], 1, &argv, &result);
return result;
}

3.ArkTS侧调用C/C++方法实现

ArkTS侧通过import引入Native侧包含处理逻辑的so来使用C/C++的方法。

// entry/src/main/ets/pages/Index.ets
// 通过import的方式,引入Native能力。
import nativeModule from 'libentry.so'@Entry
@Component
struct Index {
@State message: string = 'Test Node-API callNative result: ';
@State message2: string = 'Test Node-API nativeCallArkTS result: ';
build() {
Row() {
Column() {
// 第一个按钮,调用add方法,对应到Native侧的CallNative方法,进行两数相加。
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message += nativeModule.callNative(2, 3);
})
// 第二个按钮,调用nativeCallArkTS方法,对应到Native的NativeCallArkTS,在Native调用ArkTS function。
Text(this.message2)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message2 += nativeModule.nativeCallArkTS((a: number)=> {
return a * 2;
});
})
}
.width('100%')
}
.height('100%')
}
}

4.Node-API的约束限制

1)SO命名规则

导入使用的模块名和注册时的模块名大小写保持一致,如模块名为entry,则so的名字为libentry.so,napi_module中nm_modname字段应为entry,ArkTS侧使用时写作:import xxx from 'libentry.so'。

2)注册建议

  • nm_register_func对应的函数(如上述Init函数)需要加上static,防止与其他so里的符号冲突。

  • 模块注册的入口,即使用__attribute__((constructor))修饰的函数的函数名(如上述RegisterDemoModule函数)需要确保不与其它模块重复。

3)多线程限制

每个引擎实例对应一个JS线程,实例上的对象不能跨线程操作,否则会引起应用crash。使用时需要遵循如下原则:

  • Node-API接口只能在JS线程使用。

  • Native接口入参env与特定JS线程绑定只能在创建时的线程使用。

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

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

相关文章

mac环境下,ollama+deepseek+cherry studio+chatbox本地部署

春节期间,deepseek迅速火爆全网,然后回来上班,我就浅浅的学习一下,然后这里总结一下,我学习中,总结的一些知识点吧,分享给大家。具体的深度安装部署,这里不做赘述,因为网…

深度学习01 神经网络

目录 神经网络 ​感知器 感知器的定义 感知器的数学表达 感知器的局限性 多层感知器(MLP, Multi-Layer Perceptron) 多层感知器的定义 多层感知器的结构 多层感知器的优势 偏置 偏置的作用 偏置的数学表达 神经网络的构造 ​神经网络的基本…

电脑右下角小喇叭没反应怎么回事,快速解决方案

当电脑右下角的小喇叭(音量图标)没有反应时,可以尝试以下快速解决方案: 一、基础检查与操作 检查键盘音量键: 按下键盘上的音量增加或减少键,或尝试Fn音量键(部分笔记本需组合键)&a…

P3654 First Step (ファーストステップ)(贪心算法)

#include<bits/stdc.h> using namespace std;int main() {int r,c,k;cin>>r>>c>>k;char a[105][105];int ans0;for(int i0;i<r;i){for(int j0;j<c;j){cin>>a[i][j];}}for(int i0;i<r;i){int cnt0; // 用来记录连续空地的数量for(int j…

ASP.NET Core对JWT的封装

目录 JWT封装 [Authorize]的注意事项 JWT封装 NuGet 库 |Microsoft.AspNetCore.Authentication.JwtBearer 9.0.1https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearerhttps://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBea…

【热点文章-定时计算】分布式任务调度框架xxl-job

分布式任务调度 在分布式架构下&#xff0c;一个服务会部署多个实例来运行业务&#xff1b;如果在这种分布式系统环境下运行任务调度&#xff0c;称为分布式任务调度。 分布式任务调度框架&#xff1a;xxl-job xxl-job环境搭建 本机 仓库源码&#xff1a;xxl-job 初始化…

嵌入式面试题 C/C++常见面试题整理_7

一.什么函数不能声明为虚函数? 常见的不能声明为虚函数的有:普通函数(非成员函数):静态成员函数;内联成员函数;构造函数;友元函数。 1.为什么C不支持普通函数为虚函数?普通函数(非成员函数)只能被overload&#xff0c;不能被override&#xff0c;声明为虚函数也没有什么意思…

【MySQL】深入了解索引背后的内部结构

目录 索引的认识&#xff1a; 作用&#xff1a; 索引的使用&#xff1a; 索引底层的数据结构&#xff1a; 哈希表 AVL树 红黑树 B树&#xff1a; B树&#xff1a; B树搜索&#xff1a; 索引的认识&#xff1a; 索引是数据库中的一个数据结构&#xff0c;用于加速查询…

最新版Node.js下载安装指定版本图文版教程(非常详细)

文字目录 1、什么是Node.js&#xff1f;2、什么是 npm&#xff1f;3、下载Node.js安装包4、详细安装步骤 1、什么是Node.js&#xff1f; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;它使 JavaScript 可以在服务器端运行。在 Node.js 出现之前&#…

Spring Boot Web 入门

目录 Spring Boot Web 是 Spring Boot 框架的一个重要模块&#xff0c;它简化了基于 Spring 的 Web 应用程序的开发过程。以下是一个 Spring Boot Web 项目的入门指南&#xff0c;涵盖了项目创建、代码编写、运行等关键步骤。 1. 项目创建 使用 Spring Initializr 使用 IDE …

深度解读 Docker Swarm

一、引言 随着业务规模的不断扩大和应用复杂度的增加,容器集群管理的需求应运而生。如何有效地管理和调度大量的容器,确保应用的高可用性、弹性伸缩和资源的合理分配,成为了亟待解决的问题。Docker Swarm 作为 Docker 官方推出的容器集群管理工具,正是在这样的背景下崭露头…

eggnog后kegg结果提取和注释

首先进入KEGG BRITE: KEGG Orthology (KO) 下载json文件 用python处理一下 import json import re import osos.chdir("C:/Users/fordata/Downloads/") with open("ko00001.json","r") as f:fj f.read()kojson json.loads(fj)with open(&qu…

qt6.8安装mysql8.0驱动

qt6.8安装mysql8.0驱动 qt6.8本身是不带mysql驱动。想要在qt里面使用mysql,还是比较麻烦的。需要自己编译驱动 首先下载qt源码&#xff0c;链接Index of /archive/qt/6.8/6.8.1/single 下载mysql对于驱动文件&#xff0c;链接是MySQL :: Download MySQL Connector/C (Archiv…

学习threejs,使用Lensflare模拟镜头眩光

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.Lensflare 二、&…

Cortex-M异常处理深度解析:从HardFault到调试实战

文章目录 引言一、Cortex-M异常类型全景图二、关键寄存器:故障分析的“密码本”1. HardFault状态寄存器(HFSR, 0xE000ED2C)2. 可配置故障状态寄存器(CFSR, 0xE000ED28)3. 地址寄存器(BFAR/MMFAR)三、调试实战:从寄存器到代码定位四、自定义HardFault处理程序五、Keil与…

【大模型】DeepSeek与chatGPT的区别以及自身的优势

目录 一、前言二、核心技术对比2.1 模型架构设计2.1.1 ChatGPT的Transformer架构2.1.2 DeepSeek的混合架构 2.2 训练数据体系2.2.1 ChatGPT的数据特征2.2.2 DeepSeek的数据策略 三、应用场景对比3.1 通用场景表现3.1.1 ChatGPT的强项领域3.2.2 DeepSeek的专项突破 3.3 响应效率…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(二)

接上篇&#xff1a;《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》 链接 文章目录 4.安装RabbitMQ Messaging Topology Operator 裸金属环境部署RabbitMQ部署单实例部署集群 4.安装RabbitMQ Messaging Topology Operator 使用 cer…

低至3折,百度智能云千帆宣布全面支持DeepSeek-R1/V3调用

DeepSeek-R1和 DeepSeek-V3模型已在百度智能云千帆平台上架 。 出品|产业家 新年伊始&#xff0c;百度智能云又传来新动作 。 2月3日百度智能云宣布&#xff0c; DeepSeek-R1和 DeepSeek-V3模型已在百度智能云千帆平台上架&#xff0c;同步推出超低价格方案&#xff0c;并…

STM32G0B1 ADC DMA normal

目标 ADC 5个通道&#xff0c;希望每1秒采集一遍&#xff1b; CUBEMX 配置 添加代码 #define ADC1_CHANNEL_CNT 5 //采样通道数 #define ADC1_CHANNEL_FRE 3 //单个通道采样次数&#xff0c;用来取平均值 uint16_t adc1_val_buf[ADC1_CHANNEL_CNT*ADC1_CHANNEL_FRE]; //传递…

【翻译+论文阅读】DeepSeek-R1评测:粉碎GPT-4和Claude 3.5的开源AI革命

目录 一、DeepSeek-R1 势不可挡二、DeepSeek-R1 卓越之处三、DeepSeek-R1 创新设计四、DeepSeek-R1 进化之路1. 强化学习RL代替监督微调学习SFL2. Aha Moment “啊哈”时刻3. 蒸馏版本仅采用SFT4. 未来研究计划 部分内容有拓展&#xff0c;部分内容有删除&#xff0c;与原文会有…