本文主要讲解如何在鸿蒙原生应用开发中接入并使用 node-addon-api 以实现 C/C++ 与上层 ArkTS 之间的桥接。
node-addon-api 适配仓库地址: node-addon-api-ohos
背景
笔者在过去一年多的时间里主要精力聚焦于 Rust 在鸿蒙生态的一些适配工作。但是在过往的交流中发现一些问题,最主要的问题在于:鸿蒙官方主推的 Native 模块开发语言还是 C/C++,部分开发者或者公司仍然想要基于 C/C++ 来做具体的功能开发(公司 C/C++ 能力储备远高于 Rust)。
而原子 N-API 由于其复杂和冗余的工作量,因此我们仍然需要一些上层的封装框架来减少我们的开发工作量。
在官方社区中,提供了 Aki 作为上层的开发框架,不过在笔者过去接入的一些经验来看,该框架存在以下问题:
- 能力不够全面,比如手动将函数封装成 ThreadsafeFunction 等能力不支持。
- 设计复杂,包体积较大。
- 功能不够稳定,缺少单元测试对能力进行测试。
而 Node.js 官方也维护了基于 N-API 的上层开发框架 node-addon-api,与 Aki 相比我认为最重要的部分是:它提供了全面的单元测试,对所有提供的能力进行了完善的测试,以确保能力的稳定性和可用性。同时这也是我们迁移鸿蒙适配的基础保证,通过迁移单元测试能够确保在鸿蒙系统上能力的稳定性和可用性。
因此在最近的一段时间,笔者也开始了尝试迁移 node-addon-api。
目前 node-addon-api 已经可以使用较为简单的方案进行接入了,接入下来我们使用一个简单的示例来展示如何在鸿蒙系统中使用。
本文的示例代码在:node-addon-api-example
使用与示例
接入
首先依旧是创建一个 Native C++ 的模板项目,这有助于我们减少自己的一些工作。
在创建完成之后的项目中,我们只需要进入到需要开发 Native 模块的具体模块文件夹中安装包即可。
比如在本项目中我们在 entry 模块中开发一个两数之和的 Native 模块,那么我们就需要进入到 entry 目录下执行如下命令即可:
ohpm install @ohos-rs/node-addon-api --save-dev
紧接着我们进入到 C++ 模块文件下修改 CMakeLists.txt
文件以接入 node-addon-api
cmake_minimum_required(VERSION 3.5.0)
project(MyApplication3)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
# 设置包所在的路径,注意设置的变量名必须为如下所示,否则在 find_package 的时候将会报错
set(NODE_ADDON_API_OHOS_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@ohos-rs/node-addon-api)
# 设置 find_package 的查询路径
set(CMAKE_MODULE_PATH ${NODE_ADDON_API_OHOS_ROOT_PATH})
# 通用 find_package 实现头文件以及模块的查询以及引入
find_package(node_addon_api_ohos REQUIRED)
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp)
# entry 模块接入 node-addon-api
target_link_libraries(entry PUBLIC libace_napi.z.so node_addon_api_ohos)
这里我们就完成了在鸿蒙工程中接入 node-addon-api 的所有操作,这时候 IDE 会提醒我们同步项目工程,我们只需要点击 Sync Now
即可。
开发
接下来我们就可以在项目中使用 node-addon-api 了,接下来我们基于 node-addon-api 实现一个两数之和的 native 模块。
#include <napi.h>
// 两数之和的函数
Napi::Number Add(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// 检查参数数量和类型
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) {
Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
return Napi::Number::New(env, 0);
}
// 获取参数并计算和
double arg0 = info[0].As<Napi::Number>().DoubleValue();
double arg1 = info[1].As<Napi::Number>().DoubleValue();
double sum = arg0 + arg1;
// 返回结果
return Napi::Number::New(env, sum);
}
// 初始化模块
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));
return exports;
}
NODE_API_MODULE(entry, Init)
将 napi_init.cpp
文件中的代码修改为如上代码所示即可,这里有几点需要注意:
- NODE_API_MODULE 的第一个参数必须与你需要构建的模块名一致,不可以是任意的名称。
- 目前已经做了简单的适配: 移除 Symbol 支持和移除 napi_adjust_external_memory 这两个能力。
现在我们只需要点击运行,看看我们最终的执行效果是否符合预期即可。
可以看到最终运行效果是完美符合预期的,相较于使用原子 N-API 的代码来说,使用 node-addon-api 进行开发,其代码量更少且逻辑清晰。
问题
当然目前 node-addon-api 的适配工作仍在进行中,还有许多工作需要解决。
1. 单测未能完全迁移
目前已经迁移了一部分单元测试,但是仍然有 70% 以上的单测尚未完成迁移,这个还需要一定的时间去完成。同时迁移过程中也是不断在发现和修复问题,因此整体工作时间仍然需要较长的时间。
2. 部分需要鸿蒙额外适配的工作尚未完成
同第一个问题,目前仅适配了两个已知且清晰的适配项,剩下的适配工作需要在单测迁移的过程中进行,以确保能力的稳定性和可用性。
3. 鸿蒙拓展能力尚未新增
鸿蒙提供了需要平台特有的拓展能力,比如 ArkRuntime
以及 QOS
调度。这部分能力目前也还没有开发完成。
总结
目前 node-addon-api 在鸿蒙上的基本流程已经完成,但是仍然有大量的工作需要完成。我们将与 ohos-rs 一样保持对上游能力的尽可能完善的适配和可用,也希望社区能够一起参与适配和完善工作。希望本文对你有所帮助~