鸿蒙使用 node-addon-api 开发原生模块

62 阅读5分钟

本文主要讲解如何在鸿蒙原生应用开发中接入并使用 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 作为上层的开发框架,不过在笔者过去接入的一些经验来看,该框架存在以下问题:

  1. 能力不够全面,比如手动将函数封装成 ThreadsafeFunction 等能力不支持。
  2. 设计复杂,包体积较大。
  3. 功能不够稳定,缺少单元测试对能力进行测试。

而 Node.js 官方也维护了基于 N-API 的上层开发框架 node-addon-api,与 Aki 相比我认为最重要的部分是:它提供了全面的单元测试,对所有提供的能力进行了完善的测试,以确保能力的稳定性和可用性。同时这也是我们迁移鸿蒙适配的基础保证,通过迁移单元测试能够确保在鸿蒙系统上能力的稳定性和可用性。

因此在最近的一段时间,笔者也开始了尝试迁移 node-addon-api。

目前 node-addon-api 已经可以使用较为简单的方案进行接入了,接入下来我们使用一个简单的示例来展示如何在鸿蒙系统中使用。

本文的示例代码在:node-addon-api-example

使用与示例

接入

首先依旧是创建一个 Native C++ 的模板项目,这有助于我们减少自己的一些工作。

17299115299880.jpg

在创建完成之后的项目中,我们只需要进入到需要开发 Native 模块的具体模块文件夹中安装包即可。

比如在本项目中我们在 entry 模块中开发一个两数之和的 Native 模块,那么我们就需要进入到 entry 目录下执行如下命令即可:

ohpm install @ohos-rs/node-addon-api --save-dev

17299118385912.jpg

紧接着我们进入到 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 即可。

17299121137636.jpg

开发

接下来我们就可以在项目中使用 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 文件中的代码修改为如上代码所示即可,这里有几点需要注意:

  1. NODE_API_MODULE 的第一个参数必须与你需要构建的模块名一致,不可以是任意的名称。
  2. 目前已经做了简单的适配: 移除 Symbol 支持和移除 napi_adjust_external_memory 这两个能力。

现在我们只需要点击运行,看看我们最终的执行效果是否符合预期即可。

17299125159885.jpg

可以看到最终运行效果是完美符合预期的,相较于使用原子 N-API 的代码来说,使用 node-addon-api 进行开发,其代码量更少且逻辑清晰。

问题

当然目前 node-addon-api 的适配工作仍在进行中,还有许多工作需要解决。

1. 单测未能完全迁移

目前已经迁移了一部分单元测试,但是仍然有 70% 以上的单测尚未完成迁移,这个还需要一定的时间去完成。同时迁移过程中也是不断在发现和修复问题,因此整体工作时间仍然需要较长的时间。

2. 部分需要鸿蒙额外适配的工作尚未完成

同第一个问题,目前仅适配了两个已知且清晰的适配项,剩下的适配工作需要在单测迁移的过程中进行,以确保能力的稳定性和可用性。

3. 鸿蒙拓展能力尚未新增

鸿蒙提供了需要平台特有的拓展能力,比如 ArkRuntime 以及 QOS 调度。这部分能力目前也还没有开发完成。

总结

目前 node-addon-api 在鸿蒙上的基本流程已经完成,但是仍然有大量的工作需要完成。我们将与 ohos-rs 一样保持对上游能力的尽可能完善的适配和可用,也希望社区能够一起参与适配和完善工作。希望本文对你有所帮助~