一、 NAPI框架概述
NAPI(Native API)是鸿蒙生态中用于实现ArkTS/JS与C/C++代码互操作的核心框架。其概念源自Node.js,为了实现JavaScript脚本与C++库之间的相互调用,Node.js对V8引擎的API做了一层封装,称为NAPI。
OpenHarmony系统沿用了NAPI的接口定义形式,但每个接口的内部实现都进行了重写。这是因为NAPI接口的本质是帮助C++程序去跟JavaScript引擎交互,对于不同的引擎需要有不同的实现方式。当用户调用了NAPI接口(如 napi_create_int64()
),在Node.js中它会访问V8引擎的API,而在OpenHarmony中,它访问的是ArkUI框架自己的JS引擎。
NAPI主要解决以下问题:
- 性能需求:对于CPU密集型、IO密集型或需要直接操作硬件的场景,C/C++实现通常比纯ArkTS/JS更高效。
- 代码复用:允许将现有的C/C++库(如音视频编解码、加密算法等)快速移植到鸿蒙平台。
- 系统级访问:提供对操作系统底层功能的访问能力,如GPIO控制、特定硬件驱动交互等。
二、 OpenHarmony与HarmonyOS NAPI开发的核心区别
虽然两者都使用NAPI框架,但其目标、开发流程和部署方式有本质区别。
特性 | OpenHarmony (系统级扩展) | HarmonyOS (应用级模块) |
---|---|---|
目标 | 扩展系统能力,为所有应用提供新的原生API。 | 增强单个应用,封装应用私有的高性能逻辑。 |
开发环境 | 需要完整的OpenHarmony源码和Linux编译环境。 | 使用DevEco Studio,无需系统源码。 |
SDK依赖 | 依赖并需要修改Full-SDK,将新接口打包进系统镜像。 | 使用Public-SDK,模块随应用打包。 |
构建系统 | 使用GN 和ohos.build配置,参与系统编译。 | 使用CMake配置,由DevEco Studio编译。 |
模块位置 | 编译后.so文件位于/system/lib/module/ 目录,成为系统一部分。 |
编译后.so文件打包在HAP内,位于应用私有目录。 |
ArkTS导入 | import myApi from '@ohos.myNewApi' (标准系统接口名)。 |
import nativeModule from 'libentry.so' (直接指定so名)。 |
适用场景 | OEM厂商、系统开发者,为特定硬件或平台能力提供标准接口。 | 应用开发者,优化应用内算法、复用现有C/C++库。 |
OpenHarmony的NAPI开发是一种"自下而上"的模式,开发者修改系统源码,增加一个新的子系统/组件,编译后生成包含新原生接口的系统固件和Full-SDK。应用开发者拿到这个新的SDK后,就能像使用系统官方API一样使用新增的接口。而HarmonyOS的NAPI开发是一种"自上而下"的模式,开发者在应用工程中直接创建一个原生模块,这个模块只为当前应用服务,与系统其他部分隔离。
三、 OpenHarmony NAPI开发:新增系统API
以下步骤演示如何在OpenHarmony源码中新增一个名为hellonapi
的系统API模块,提供一个简单的add
函数。
(一) 环境准备
- OpenHarmony源码。
- Linux编译环境(如Ubuntu 18.04/20.04)。
- 预装编译工具链。
(二) 新增子系统与组件
-
创建子系统目录 :在源码根目录创建
mysubsys
文件夹。 -
配置子系统 :在
mysubsys
下创建ohos.build
文件,定义子系统和组件。json// mysubsys/ohos.build { "subsystem": "mysubsys", "parts": { "hello": { "module_list": [ "//mysubsys/hello/hellonapi:hellonapi" ] } } }
-
注册子系统 :修改
build/subsystem_config.json
,将新子系统加入构建系统。json// build/subsystem_config.json (片段) "mysubsys": { "path": "mysubsys", "name": "mysubsys" },
(三) 实现NAPI模块
-
创建目录结构 :
mysubsys/ └── hello/ └── hellonapi/ ├── hellonapi.cpp └── BUILD.gn
-
编写C++代码 (
hellonapi.cpp
) :cpp#include "napi/native_api.h" #include "napi/native_node_api.h" // C++业务实现函数 static napi_value Add(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); if (argc < 2) { napi_throw_error(env, nullptr, "Invalid argument count. Expected 2."); return nullptr; } napi_valuetype valuetype0; napi_typeof(env, args[0], &valuetype0); napi_valuetype valuetype1; napi_typeof(env, args[1], &valuetype1); if (valuetype0 != napi_number || valuetype1 != napi_number) { napi_throw_type_error(env, nullptr, "Wrong argument type. Numbers expected."); return 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 Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr} }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; } // 模块定义 static napi_module hellonapiModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "hellonapi", // 模块名,对应ArkTS导入名的一部分 .nm_priv = ((void*)0), .reserved = {0}, }; // 模块注册,加载so时自动调用 extern "C" __attribute__((constructor)) void RegisterHellonapiModule(void) { napi_module_register(&hellonapiModule); }
-
编写构建脚本 (
BUILD.gn
) :gnimport("//build/ohos.gni") ohos_shared_library("hellonapi") { include_dirs = [ "//foundation/ace/napi/interfaces/kits" ] sources = [ "hellonapi.cpp" ] deps = [ "//foundation/ace/napi:ace_napi", ] relative_install_dir = "module" // 关键:指定安装到/system/lib/module/ subsystem_name = "mysubsys" part_name = "hello" }
(四) 编译与集成
-
编译系统 :执行
./build.sh --product-name <你的产品名>
(如rk3568
)。 -
生成Full-SDK :编译成功后,新的
libhellonapi.z.so
会被打包到系统镜像中,并生成对应的接口声明文件(.d.ts
)到out/<product>/packages/phone/samples/full-sdk
目录。 -
ArkTS应用调用 :应用开发者将这个新的Full-SDK集成到DevEco Studio中,就可以像使用系统API一样调用它:
typescriptimport hellonapi from '@ohos.hellonapi'; // 注意:模块名通常是 @ohos. + nm_modname @Entry @Component struct Index { @State result: number = 0; build() { Row() { Column() { Text(`Result: ${this.result}`) Button('Add 5 + 3') .onClick(() => { this.result = hellonapi.add(5, 3); }) } } } }
四、 HarmonyOS NAPI开发:创建应用原生模块
以下步骤演示如何在HarmonyOS应用工程中创建一个原生模块。
(一) 环境准备
- DevEco Studio。
- HarmonyOS SDK(Public-SDK即可)。
(二) 创建Native工程
- 在DevEco Studio中,选择 File > New > Create Project。
- 选择模板 Native C++,点击Next。
- 配置工程名、包名等,点击Finish。
- DevEco Studio会自动生成一个包含
napi-init.cpp
、CMakeLists.txt
和示例ArkTS代码的工程。
(三) 实现NAPI模块
-
编写C++代码 :在
entry/src/main/cpp
目录下,修改或创建.cpp
文件(例如hello.cpp
)。cpp// entry/src/main/cpp/hello.cpp #include "napi/native_api.h" // 与OpenHarmony示例中的Add函数完全相同 static napi_value Add(napi_env env, napi_callback_info info) { // ... (代码同上) ... } // 此函数由napi-init.cpp中的Init函数调用 napi_value CreateAddFunction(napi_env env) { napi_property_descriptor desc[] = { {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr} }; napi_value result; napi_create_object(env, &result); napi_define_properties(env, result, sizeof(desc) / sizeof(desc[0]), desc); return result; }
-
注册接口 :打开
entry/src/main/cpp/napi-init.cpp
,修改Init
函数来导出你的接口。cpp// entry/src/main/cpp/napi-init.cpp #include "napi/native_api.h" // 声明你的函数创建器 extern napi_value CreateAddFunction(napi_env env); static napi_value Init(napi_env env, napi_value exports) { // 将你的函数对象挂载到exports上 napi_set_named_property(env, exports, "addModule", CreateAddFunction(env)); return exports; } // ... (其余自动生成的代码保持不变) ...
-
配置构建脚本 (
CMakeLists.txt
) :cmake# entry/src/main/cpp/CMakeLists.txt cmake_minimum_required(VERSION 3.4.1) project(Entry) set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) if(DEFINED PACKAGE_FIND_FILE) include(${PACKAGE_FIND_FILE}) endif() include_directories(${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/include) add_library(entry SHARED hello.cpp napi-init.cpp) # 添加你的cpp文件 find_package(hilog REQUIRED CONFIG) find_package(ace_napi REQUIRED CONFIG) target_link_libraries(entry PUBLIC ace_napi::ace_napi hilog::libhilog)
(四) ArkTS应用调用
-
编译项目 :DevEco Studio会自动编译C++代码生成
libentry.so
并打包到HAP中。 -
在ArkTS中调用 :
typescript// entry/src/main/ets/pages/Index.ets import nativeModule from 'libentry.so'; // 直接导入so文件名 @Entry @Component struct Index { @State result: number = 0; build() { Row() { Column() { Text(`Result: ${this.result}`) Button('Add 10 + 20') .onClick(() => { // 通过在Init中挂载的对象名访问 this.result = nativeModule.addModule.add(10, 20); }) } } } }
五、 NAPI异步开发:Callback与Promise
对于耗时操作,必须使用异步模型避免阻塞UI线程。NAPI提供了基于napi_create_async_work
的异步支持。
异步工作原理
- 立即返回:原生函数被调用时,立即创建一个异步工作项并返回一个Promise对象或接收一个Callback函数,然后立即结束。
- 后台执行 :
napi_create_async_work
创建的工作项被放入线程池,在后台Worker线程中执行execute
回调。注意:execute
回调中不能调用任何NAPI接口。 - 结果返回 :
execute
执行完毕后,在JS主线程(EventLoop)中触发complete
回调。complete
回调可以将结果转换为JS值,并通过Promise的resolve
/reject
或调用Callback函数返回给ArkTS。
Promise模式示例
cpp
// 结构体用于在execute和complete之间传递数据
struct AddAsyncData {
napi_async_work work;
double x;
double y;
double result;
napi_ref callback_ref; // 如果是Callback模式,需要保存回调函数的引用
};
// execute回调:在worker线程执行
static void ExecuteAdd(napi_env env, void* data) {
AddAsyncData* asyncData = (AddAsyncData*)data;
asyncData->result = asyncData->x + asyncData->y;
}
// complete回调:在JS线程执行
static void CompleteAdd(napi_env env, napi_status status, void* data) {
AddAsyncData* asyncData = (AddAsyncData*)data;
napi_value result;
napi_create_double(env, asyncData->result, &result);
// 获取Promise的resolve函数
napi_value promise;
napi_get_named_property(env, asyncData->work, "promise", &promise);
napi_value resolve;
napi_get_named_property(env, promise, "resolve", &resolve);
// 调用resolve,将结果返回
napi_call_function(env, undefined, resolve, 1, &result, nullptr);
// 清理资源
napi_delete_async_work(env, asyncData->work);
delete asyncData;
}
// 导出的异步函数
static napi_value AddAsync(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);
// 获取参数
AddAsyncData* asyncData = new AddAsyncData();
napi_get_value_double(env, args[0], &asyncData->x);
napi_get_value_double(env, args[1], &asyncData->y);
// 创建Promise
napi_value promise;
napi_create_promise(env, &asyncData->callback_ref, &promise, &resolve); // 此处简化,实际需管理deferred
// 创建异步工作项
napi_value resource_name;
napi_create_string_utf8(env, "AddAsync", NAPI_AUTO_LENGTH, &resource_name);
napi_create_async_work(env, nullptr, resource_name, ExecuteAdd, CompleteAdd, asyncData, &asyncData->work);
// 将工作项放入队列
napi_queue_async_work(env, asyncData->work);
return promise; // 返回Promise对象
}
Callback模式的实现类似 ,只是在AddAsync
中获取并保存Callback函数的引用,在CompleteAdd
中调用该Callback即可。
六、 总结
NAPI是鸿蒙生态连接ArkTS应用层与C/C++系统/原生层的强大桥梁。通过本教程,我们清晰地认识到:
- OpenHarmony的NAPI开发是系统级工程,旨在为整个生态扩展标准化的原生能力,流程复杂但影响深远,适合平台和硬件开发者。
- HarmonyOS的NAPI开发是应用级工程,旨在优化单个应用的性能或复用现有代码库,流程简单直接,适合广大应用开发者。