Openharmony应用NAPI详解--基础篇

OpenHarmony中的NAPI:JS与C++交互框架解析
NAPI是OpenHarmony中的一个组件,用于实现JS和C++之间的交互。它提供了一套基于Node.jsN-API规范的接口,支持同步和异步调用,包括回调和Promise方式。文章通过代码示例展示了如何定义和使用这些接口,以及在C++中实现接口的方法和数据类型转换过程。

NAPI是什么?

简单点理解就是在Openharmony里,实现上层js或ets应用与底层C/C++之间交互的框架。

Openharmony里的官方解释:NAPI(Native API)组件是一套对外接口基于Node.js N-API规范开发的原生模块扩展开发框架。还有NAPI适合封装IO、CPU密集型、OS底层等能力并对外暴露JS接口,通过NAPI可以实现JS与C/C++代码互相访问。我们可以通过NAPI接口构建例如网络通信、串口访问、多媒体解码、传感器数据收集等模块。

now,以Openharmon V3.1-Release版本为例,抽取代码实例,分为两部份进行分析。

面向上层js或ets接口

1.定义接口

接口描述文件在Openharmony里.d.ts后缀结束。

在分布式文件服务(//foundation/filemanagement/dfs_service)的@ohos.sendfile.d.ts文件中

import {AsyncCallback, Callback} from "./basic";

declare namespace SendFile {
  // 异步接口----回调方式
  function sendFile(deviceId: string, sourPath: Array<string>, destPath: Array<string>, fileCount: number, callback: AsyncCallback<number>);
  // 异步接口----Promise方式
  function sendFile(deviceId: string, sourPath: Array<string>, destPath: Array<string>, fileCount: number): Promise<number>;
  // 异步接口----回调方式
  function on(type: 'sendFinished' | 'receiveFinished', callback: AsyncCallback<TransResult>): void;
  // 同步接口
  function off(type: 'sendFinished' | 'receiveFinished'): void;
}

export default SendFile;

以上为应用开发者提供了四个的JS接口,分为同步接口与异步接口两类,异步接口又分为回调方式与Promise。

2.App应用开发者的JS代码简单导入一下模块就可以直接调用了。

// 同步调用
import myapp1 from "@ohos.SendFile"
var result = myapp1.off(/* 参数自填*/);

// 异步调用
/** call back 方式的异步调用 */
myapp1.sendFile("xxxx", sourPath, destPath, 4, function (err, ret) {
    ......
});
/** Promise 方式的异步调用 */
var  promiseObj = myapp1.sendFile("xxxx", sourPath, destPath, 4).then(data => {
    ......
}).catch(error => {
    ......
});

3.关于d.ts的注意事项

1.自定义d.ts接口文件时,在应用开发工具(DevEco Studio)里需要将此文件放入Openharmony sdk所在目录中,例如,在作者的本机为D:\Program\Huawei\OpenHarmony3.1\Sdk\ets\3.1.5.5\api与D:\Program\Huawei\OpenHarmony3.1\Sdk\js\3.1.5.5\api,根据应用调用者(js或ets)的不同放入不同的目录。

2.Openharmony已有d.ts接口文件,根据文件中注释里说的@since 9对应相应的导入sdk版本后,不需要另做导入dts。

面向C++的接口实现

1.模块注册与方法名映射

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value SendFileExport(napi_env env, napi_value exports)
{
    const char className[] = "SendFile";
    static napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("sendFile", JsSendFile),
        DECLARE_NAPI_FUNCTION("on", JsOn),
        DECLARE_NAPI_FUNCTION("off", JsOff),
    };
    napi_value sendFileClass = nullptr;

    napi_define_class(env, className, sizeof(className), JsConstructor, nullptr,
        sizeof(desc) / sizeof(desc[0]), desc, &sendFileClass);

    napi_set_named_property(env, exports, "SendFile", sendFileClass);

    SendFile::RegisterCallback();
    return exports;
}
// 模块注册
NAPI_MODULE(sendfile, SendFileExport)
  • 通过NAPI_MODULE与DECLARE_NAPI_FUNCTION两个宏分别完成模块注册与方法名映射。

  • 这里dts定义了两个sendFile接口,为何只需要定义映射一个?

因为对js或ets来说sendFile对外接口名相同,在C++实现时根据传入的参数个数或者参数类型来判断js或ets调用哪个函数。

2.同步接口

根据映射,同步接口off对应C++的实现是JsOff。

  • 函数声明

每一个映射后的函数,必须是参数napi_env env, napi_callback_info cbinfo,返回值为napi_value。

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
......
}

为了实现js或ets调用,NAPI框架需要解决以下问题:数据传递与转换

js/ets传入的入参、得到的返回结果,需要转换成C/C++代码可以操作的数据类型,NAPI框架引入了一个中间的数据类型,来分别对应上层js/ets与C/C++的类型,以及数据类型的操作方法。

  • 获取入参

napi_get_cb_info从cbinfo参数中js传入参数,以下为函数说明

// napi_get_cb_info从napi_callback_info类型的参数中得到回调的参数等数据
napi_status napi_get_cb_info(
  napi_env env,
  napi_callback_info cbinfo, // 传入回调函数的回调信息
  size_t *argc,             // 作为入参传入`argv`数组的大小,并将接收实际的参数个数
  napi_value *argv,         // 存放参数的buffer
  napi_value *this_arg,     // Javascript中的`this`
  void** data               // 接收数据指针
);

argc为传入参数个数,argv为传入参数数组,此时参数的类型都为napi_value。

通过napi_typeof方法获取入参的实际类型

// napi_typeof获取指定对象的类型
napi_status napi_typeof(
  napi_env env,
  napi_value value,         // 将要获取类型的Javascript值
  napi_valuetype *result    // Javascript值的类型
);

根据d.ts的描述,off传入的只有一个参数,参数类型为字符串。

// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
    size_t requireArgc = 1;
...
    NAPI_ASSERT(env, argc == requireArgc, "requires 1 parameter"); //参数个数为1 
    napi_valuetype eventValueType = napi_undefined;
	napi_typeof(env, argv[0], &eventValueType);
	NAPI_ASSERT(env, eventValueType == napi_string, "type mismatch for parameter 1");//参数类型为napi_string,即为NAPI中定义一种字符串
...
}
  • 类型转换为C/C++可识别的类型

napi_get_value_string_utf8方法将napi_string转换char*

napi_status napi_get_value_string_utf8(napi_env env,
  napi_value value,
  char* buf,
  size_t bufsize,
  size_t* result);
// foundation/filemanagement/dfs_service/frameworks/js/napi/src/sendfile_napi.cpp
napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
...
    char* type = nullptr;
    size_t typeLen = 0;
    napi_get_value_string_utf8(env, argv[0], nullptr, 0, &typeLen);

    NAPI_ASSERT(env, typeLen > 0, "typeLen == 0");
    type = new char[typeLen + 1];

    napi_get_value_string_utf8(env, argv[0], type, typeLen + 1, &typeLen);
...
}
  • 返回值

C++没有返回值,此时JsOff将nullptr返回。

NAPI框架没有nullptr,通过napi_get_undefined将nullptr转换成napi_undefined。

napi_value JsOff(napi_env env, napi_callback_info cbinfo)
{
...
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

后续更精彩

1.Openharmony应用NAPI详解--进阶篇

OpenHarmony 中,NAPI(Native API)是一种 JS API 的实现机制,主要用于将底层 C/C++ 代码封装为模块,并通过 JavaScript 接口对外暴露。这种机制适用于需要高性能处理、调用底层硬件能力或复用已有 C/C++ 库的场景,例如 IO 操作、CPU 密集型任务以及操作系统底层功能等[^2]。 ### NAPI 开发的基本流程 1. **环境准备** 在开始开发之前,需要确保已安装适配 OpenHarmony 的 SDK 和构建工具链。OpenHarmony 提供了 NDK(Native Development Kit),其中包含了用于编译和调试 Native 代码的工具和库[^4]。 2. **创建 Native 模块** - 编写 C/C++ 代码,实现所需的功能。 - 使用 `napi_create_function` 或其他相关接口定义 JS 可调用的方法。 - 将 C/C++ 代码编译成 `.so` 动态链接库文件。这通常涉及配置 `BUILD.gn` 文件以指定编译规则和依赖项[^3]。 3. **注册 Native 模块** - 在模块初始化时,使用 `Init` 函数向 JavaScript 环境注册 Native 方法。 - 示例代码如下: ```cpp extern "C" __attribute__((visibility("default"))) void OHOS__JS__ModuleNameInit(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { { "nativeFunction", nullptr, NativeFunction, nullptr, nullptr, 0, napi_default, nullptr }, }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); } ``` - 上述函数中,`nativeFunction` 是 JavaScript 层可调用的方法名,`NativeFunction` 是对应的 C/C++ 实现[^2]。 4. **打包与集成** - 将编译生成的 `.so` 文件与 JS 接口代码一起打包为 `.har` 模块包。 -应用项目中引用该 `.har` 包,即可在 JS 代码中调用 Native 方法[^3]。 5. **调试与优化** - 使用 LLDB 进行远程调试,OpenHarmony 支持通过 `lldb-server` 启动调试会话并连接到调试器。 - 利用 `musl` 库的日志输出功能进行问题排查[^4]。 ### NAPI应用场景 - **性能敏感型任务**:如图像处理、音频编码/解码等,这些任务可以通过 Native 代码提升执行效率。 - **复用现有 C/C++ 库**:避免重复造轮子,直接利用已有的开源库。 - **特定硬件加速**:例如使用 NEON 指令集优化视频解码等计算密集型操作。 ### 不推荐使用 NAPI 的场景 - **纯 Native 应用开发**:OpenHarmony 更倾向于基于 ArkTS 的开发模式,不建议完全依赖 Native 代码。 - **跨设备兼容性要求高的应用**:不同设备可能支持的 Native API 存在差异,可能导致兼容性问题[^4]。 ### 示例:NAPI 调用流程 假设我们有一个简单的 Native 方法,用于返回两个整数之和: ```cpp #include <napi/native_api.h> napi_value AddNumbers(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2]; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); int32_t a, b; napi_get_value_int32(env, args[0], &a); napi_get_value_int32(env, args[1], &b); napi_value result; napi_create_int32(env, a + b, &result); return result; } extern "C" __attribute__((visibility("default"))) void OHOS__JS__MathModuleInit(napi_env env, napi_value exports) { napi_property_descriptor desc = { "add", nullptr, AddNumbers, nullptr, nullptr, 0, napi_default, nullptr }; napi_define_properties(env, exports, 1, &desc); } ``` 在 JS 层调用方式如下: ```javascript import MathModule from 'libmathmodule.so'; let sum = MathModule.add(5, 7); console.log('Sum:', sum); // 输出: Sum: 12 ``` --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值