HarmonyOS/鸿蒙PC 原生应用调用C/C++三方库:NAPI 开发完整指南
在鸿蒙应用开发中,经常会遇到需要复用已有 C/C++ 三方库的场景。ArkTS 代码都无法直接调用 C/C++ 函数——必须通过 NAPI(Node-API) 机制搭建一座桥梁。本文系统讲解 NAPI(Node-API)在 HarmonyOS 中的使用方法,从基础概念到实战开发。
更多交流学习,欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
本文对应的实战项目地址:https://atomgit.com/qq8864/LibMediaInfoDemo
文章目录
1. NAPI 是什么
NAPI(Node-API)是 HarmonyOS 提供的 C/C++ 与 ArkTS/JS 的互操作接口。它让你可以:
- 从 ArkTS 调用 C/C++ 函数:将高性能计算、三方库封装成 ArkTS 可调用的接口
- 从 C/C++ 调用 ArkTS:在 Native 侧创建 JS 对象、调用 JS 回调
- 跨语言传递数据:字符串、数字、数组、对象等双向传递
为什么需要 NAPI
| 场景 | 说明 |
|---|---|
| 性能敏感计算 | 图像处理、音视频编解码、加密运算 |
| 复用现有库 | 调用已有的 C/C++ 三方库(如 libmediainfo、OpenCV) |
| 系统级操作 | 直接操作文件描述符、内存、硬件接口 |
| 跨平台代码 | C/C++ 逻辑跨 Android/iOS/HarmonyOS 复用 |
2. NAPI 核心概念
2.1 napi_env
NAPI 的"运行环境",每个 NAPI 函数的第一个参数。代表一个 JS 虚拟机上下文,所有 NAPI 调用都依赖它。
static napi_value MyFunc(napi_env env, napi_callback_info info) {
// env 贯穿所有 NAPI 调用
}
2.2 napi_value
JS 值在 C++ 侧的"不透明句柄"。所有 ArkTS 和 C++ 之间的数据传递都通过它:
ArkTS: 42 -> C++: napi_value (通过 napi_get_value_int32 提取)
C++: "hello" -> ArkTS: string (通过 napi_create_string_utf8 创建)
napi_value 本身不携带类型信息,需要通过 napi_typeof 查询类型。
2.3 napi_callback_info
回调信息,用于从 ArkTS 调用中获取参数和 this 指针。
2.4 模块注册机制
NAPI 模块通过 napi_module 结构体注册到框架中:
static napi_module myModule = {
.nm_version = 1, // 固定为1
.nm_flags = 0, // 固定为0
.nm_filename = nullptr, // 固定为nullptr
.nm_register_func = Init, // 模块初始化函数
.nm_modname = "mymodule", // 模块名(决定import名称)
.nm_priv = ((void *)0), // 固定为0
.reserved = {0}, // 固定为0
};
extern "C" __attribute__((constructor)) void RegisterModule(void) {
napi_module_register(&myModule);
}
命名对应关系:
| nm_modname | CMake add_library | 编译产物 | ArkTS import |
|---|---|---|---|
| “mymodule” | add_library(mymodule SHARED …) | libmymodule.so | import xxx from 'mymodule.so' |
| “entry” | add_library(entry SHARED …) | libentry.so | import xxx from 'entry.so' |
3. 开发环境准备
3.1 工程配置
在 entry/build-profile.json5 中启用 Native 构建:
{
"buildOption": {
"externalNativeOptions": {
"path": "./src/main/cpp/CMakeLists.txt",
"arguments": "",
"cppFlags": ""
}
}
}
3.2 CMake 配置
cmake_minimum_required(VERSION 3.4.1)
project(MyNativeModule)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
add_library(mymodule SHARED mymodule_napi.cpp)
target_link_libraries(mymodule PUBLIC
ace_napi.z # NAPI 框架库(必须链接)
hilog_ndk.z # 日志库
)
3.3 类型声明
entry/src/main/cpp/types/mymodule/index.d.ts:
export const add: (a: number, b: number) => number;
export const greet: (name: string) => string;
entry/src/main/cpp/types/mymodule/oh-package.json5:
{
"name": "mymodule.so",
"version": "1.0.0",
"main": "index.d.ts",
"types": "index.d.ts"
}
entry/oh-package.json5:
{
"dependencies": {
"mymodule.so": "file:src/main/cpp/types/mymodule"
}
}
4. NAPI 模块开发全流程
Step 1: 编写 C++ 函数
每个 NAPI 导出函数的签名固定为:
static napi_value FunctionName(napi_env env, napi_callback_info info);
Step 2: 获取参数
size_t argc = 2; // 期望参数数量
napi_value args[2] = {nullptr}; // 参数数组
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// argc 会更新为实际传入参数数量,可做校验
Step 3: 类型转换(ArkTS -> C++)
将 napi_value 转换为 C++ 类型:
double value0, value1;
napi_get_value_double(env, args[0], &value0);
napi_get_value_double(env, args[1], &value1);
Step 4: 执行 C++ 逻辑
double result = value0 + value1;
Step 5: 类型转换(C++ -> ArkTS)
将 C++ 结果包装为 napi_value:
napi_value napiResult;
napi_create_double(env, result, &napiResult);
return napiResult;
Step 6: 注册到模块
EXTERN_C_START
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;
}
EXTERN_C_END
Step 7: ArkTS 调用
import mymodule from 'mymodule.so';
let sum = mymodule.add(3, 5); // 8
5. 数据类型转换
这是 NAPI 开发最频繁的操作。分为 ArkTS -> C++(取值)和 C++ -> ArkTS(创建)两个方向。
5.1 基本数值类型
| ArkTS 类型 | C++ 类型 | 取值(ArkTS->C++) | 创建(C+±>ArkTS) |
|---|---|---|---|
| number (int32) | int32_t | napi_get_value_int32 | napi_create_int32 |
| number (uint32) | uint32_t | napi_get_value_uint32 | napi_create_uint32 |
| number (int64) | int64_t | napi_get_value_int64 | napi_create_int64 |
| number (double) | double | napi_get_value_double | napi_create_double |
| boolean | bool | napi_get_value_bool | napi_get_boolean |
示例:
// 取值
int32_t intVal;
napi_get_value_int32(env, args[0], &intVal);
double dblVal;
napi_get_value_double(env, args[1], &dblVal);
bool boolVal;
napi_get_value_bool(env, args[2], &boolVal);
// 创建
napi_value napiInt;
napi_create_int32(env, 42, &napiInt);
napi_value napiDbl;
napi_create_double(env, 3.14, &napiDbl);
napi_value napiBool;
napi_get_boolean(env, true, &napiBool);
5.2 字符串
取值(ArkTS -> C++) – 两步法:先获取长度,再获取内容:
// 第一步:获取字符串长度
size_t strLen = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &strLen);
// 第二步:分配缓冲区,获取字符串内容
char *buf = new char[strLen + 1];
napi_get_value_string_utf8(env, args[0], buf, strLen + 1, &strLen);
// 使用 buf...
delete[] buf;
创建(C++ -> ArkTS):
napi_value napiStr;
napi_create_string_utf8(env, "Hello", NAPI_AUTO_LENGTH, &napiStr);
// NAPI_AUTO_LENGTH 表示自动计算字符串长度
// 也可以指定长度:napi_create_string_utf8(env, "Hello", 5, &napiStr);
5.3 数组
取值:
// 获取数组长度
uint32_t arrayLength = 0;
napi_get_array_length(env, args[0], &arrayLength);
// 遍历数组元素
for (uint32_t i = 0; i < arrayLength; i++) {
napi_value element;
napi_get_element(env, args[0], i, &element);
double val;
napi_get_value_double(env, element, &val);
}
创建:
napi_value napiArray;
napi_create_array(env, &napiArray);
for (uint32_t i = 0; i < count; i++) {
napi_value element;
napi_create_double(env, values[i], &element);
napi_set_element(env, napiArray, i, element);
}
5.4 对象
取值:
napi_value nameProp;
napi_get_named_property(env, args[0], "name", &nameProp);
size_t nameLen = 0;
napi_get_value_string_utf8(env, nameProp, nullptr, 0, &nameLen);
char *name = new char[nameLen + 1];
napi_get_value_string_utf8(env, nameProp, name, nameLen + 1, &nameLen);
创建:
napi_value napiObj;
napi_create_object(env, &napiObj);
napi_value nameVal;
napi_create_string_utf8(env, "Alice", NAPI_AUTO_LENGTH, &nameVal);
napi_set_named_property(env, napiObj, "name", nameVal);
napi_value ageVal;
napi_create_int32(env, 30, &ageVal);
napi_set_named_property(env, napiObj, "age", ageVal);
5.5 类型判断
在处理动态类型参数前,先判断类型:
napi_valuetype type;
napi_typeof(env, args[0], &type);
switch (type) {
case napi_number: /* 处理数字 */ break;
case napi_string: /* 处理字符串 */ break;
case napi_object: /* 处理对象或数组 */ break;
case napi_null: /* null */ break;
case napi_undefined: /* undefined */ break;
default: break;
}
// 判断是否为数组
bool isArray = false;
napi_is_array(env, args[0], &isArray);
5.6 undefined 和 null
// 创建
napi_value napiUndefined;
napi_get_undefined(env, &napiUndefined);
napi_value napiNull;
napi_get_null(env, &napiNull);
// 判断
bool isUndefined = false;
napi_is_undefined(env, value, &isUndefined);
6. 异步处理
耗时操作(文件IO、网络请求、复杂计算)不能在 NAPI 同步函数中执行,否则会阻塞 UI 线程。HarmonyOS 提供了 napi_create_async_work 来处理异步任务。
6.1 异步工作模式
ArkTS 调用异步函数
|
NAPI: napi_create_async_work() 创建异步任务
|
Execute 回调(工作线程,可执行耗时操作)
|
Complete 回调(主线程,将结果返回给ArkTS)
|
ArkTS: Promise resolve 或回调触发
6.2 Promise 模式示例
struct AsyncData {
napi_async_work work;
napi_deferred deferred; // Promise deferred
double input;
double result;
};
static void ExecuteCallback(napi_env env, void *data) {
// 在工作线程执行耗时操作
AsyncData *asyncData = (AsyncData *)data;
asyncData->result = asyncData->input * asyncData->input;
}
static void CompleteCallback(napi_env env, napi_status status, void *data) {
AsyncData *asyncData = (AsyncData *)data;
if (status == napi_ok) {
napi_value result;
napi_create_double(env, asyncData->result, &result);
napi_resolve_deferred(env, asyncData->deferred, result);
} else {
napi_value error;
napi_create_string_utf8(env, "Async work failed", NAPI_AUTO_LENGTH, &error);
napi_reject_deferred(env, asyncData->deferred, error);
}
napi_delete_async_work(env, asyncData->work);
delete asyncData;
}
static napi_value AsyncCompute(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);
// 创建 Promise
napi_value promise;
napi_deferred deferred;
napi_create_promise(env, &deferred, &promise);
// 准备异步数据
AsyncData *asyncData = new AsyncData();
asyncData->deferred = deferred;
napi_get_value_double(env, args[0], &asyncData->input);
// 创建异步工作
napi_value workName;
napi_create_string_utf8(env, "AsyncCompute", NAPI_AUTO_LENGTH, &workName);
napi_create_async_work(env, nullptr, workName,
ExecuteCallback, CompleteCallback,
asyncData, &asyncData->work);
// 加入队列
napi_queue_async_work(env, asyncData->work);
// 立即返回 Promise
return promise;
}
6.3 ArkTS 侧调用
// index.d.ts
export const asyncCompute: (input: number) => Promise<number>;
// Index.ets
let result = await mymodule.asyncCompute(5); // 25
6.4 Execute 回调中的限制
Execute回调运行在工作线程,不能调用任何 NAPI 函数,只能操作 C++ 数据Complete回调运行在主线程,可以正常调用 NAPI 函数- 如果 Execute 中需要日志,使用
HILOG_INFO/HILOG_ERROR(不依赖 napi_env)
7. 对象与回调
7.1 从 Native 调用 ArkTS 函数
static napi_value CallJsFunction(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);
// args[0] 是 ArkTS 传入的函数
napi_value jsFunc = args[0];
// 准备参数
napi_value funcArgs[2];
napi_create_int32(env, 10, &funcArgs[0]);
napi_create_int32(env, 20, &funcArgs[1]);
// 调用函数
napi_value thisArg;
napi_get_undefined(env, &thisArg);
napi_value funcResult;
napi_call_function(env, thisArg, jsFunc, 2, funcArgs, &funcResult);
// 获取返回值
int32_t intResult;
napi_get_value_int32(env, funcResult, &intResult);
napi_value napiResult;
napi_create_int32(env, intResult, &napiResult);
return napiResult;
}
ArkTS 侧:
let result = mymodule.callJsFunction((a: number, b: number) => a + b);
// result = 30
7.2 回调模式(异步 + 回调)
在异步工作的 Complete 中调用 ArkTS 回调:
static void CompleteWithCallback(napi_env env, napi_status status, void *data) {
AsyncData *asyncData = (AsyncData *)data;
napi_value callback = asyncData->callback;
napi_value result;
napi_create_double(env, asyncData->result, &result);
napi_value undefined;
napi_get_undefined(env, &undefined);
napi_value callBackResult;
napi_call_function(env, undefined, callback, 1, &result, &callBackResult);
napi_delete_async_work(env, asyncData->work);
delete asyncData;
}
8. 生命周期管理
当 C++ 对象需要与 JS 对象绑定并自动释放时,使用 napi_wrap / napi_unwrap 机制。
8.1 包装原生对象
class NativeCalculator {
public:
double memory = 0;
double Add(double a, double b) { return a + b; }
~NativeCalculator() { /* 清理资源 */ }
};
static void Destructor(napi_env env, void *nativeObject, void *hint) {
NativeCalculator *calc = (NativeCalculator *)nativeObject;
delete calc;
}
static napi_value CreateCalculator(napi_env env, napi_callback_info info) {
NativeCalculator *calc = new NativeCalculator();
napi_value jsObj;
napi_create_object(env, &jsObj);
// 将 C++ 对象绑定到 JS 对象,JS对象GC时自动调用Destructor
napi_wrap(env, jsObj, calc, Destructor, nullptr, nullptr);
return jsObj;
}
8.2 取回原生对象
static napi_value CalcAdd(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_value thisArg;
napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr);
// 从 JS 对象取出 C++ 指针
NativeCalculator *calc = nullptr;
napi_unwrap(env, thisArg, (void **)&calc);
double a, b;
napi_get_value_double(env, args[0], &a);
napi_get_value_double(env, args[1], &b);
napi_value result;
napi_create_double(env, calc->Add(a, b), &result);
return result;
}
8.3 napi_define_class(推荐方式)
用 napi_define_class 创建一个真正的 JS 类,比手动 wrap/unwrap 更规范:
static napi_value CalculatorConstructor(napi_env env, napi_callback_info info) {
napi_value thisArg;
napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr);
NativeCalculator *calc = new NativeCalculator();
napi_wrap(env, thisArg, calc, Destructor, nullptr, nullptr);
return nullptr;
}
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
{"add", nullptr, CalcAdd, nullptr, nullptr, nullptr, napi_default, nullptr},
{"clear", nullptr, CalcClear, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_value constructor;
napi_define_class(env, "Calculator", NAPI_AUTO_LENGTH,
CalculatorConstructor, nullptr,
sizeof(properties) / sizeof(properties[0]),
properties, &constructor);
napi_set_named_property(env, exports, "Calculator", constructor);
return exports;
}
ArkTS 侧:
let calc = new mymodule.Calculator();
calc.add(1, 2);
calc.clear();
9. 完整示例
一个完整的 NAPI 模块,包含同步函数、异步函数、对象封装:
#include <cstdio>
#include <cstring>
#include <string>
#include "hilog/log.h"
#include "napi/native_api.h"
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x0000
#define LOG_TAG "NapiDemo"
// ========== 1. 同步函数:两数相加 ==========
static napi_value Add(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 a, b;
napi_get_value_double(env, args[0], &a);
napi_get_value_double(env, args[1], &b);
napi_value result;
napi_create_double(env, a + b, &result);
return result;
}
// ========== 2. 同步函数:字符串处理 ==========
static napi_value Greet(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);
size_t nameLen = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &nameLen);
char *name = new char[nameLen + 1];
napi_get_value_string_utf8(env, args[0], name, nameLen + 1, &nameLen);
std::string greeting = "Hello, " + std::string(name) + "!";
delete[] name;
napi_value result;
napi_create_string_utf8(env, greeting.c_str(), greeting.length(), &result);
return result;
}
// ========== 3. 异步函数:耗时计算 ==========
struct ComputeData {
napi_async_work work;
napi_deferred deferred;
double input;
double output;
};
static void ComputeExecute(napi_env env, void *data)
{
ComputeData *cdata = (ComputeData *)data;
cdata->output = cdata->input * cdata->input * cdata->input;
}
static void ComputeComplete(napi_env env, napi_status status, void *data)
{
ComputeData *cdata = (ComputeData *)data;
if (status == napi_ok) {
napi_value result;
napi_create_double(env, cdata->output, &result);
napi_resolve_deferred(env, cdata->deferred, result);
} else {
napi_value error;
napi_create_string_utf8(env, "Compute failed", NAPI_AUTO_LENGTH, &error);
napi_reject_deferred(env, cdata->deferred, error);
}
napi_delete_async_work(env, cdata->work);
delete cdata;
}
static napi_value AsyncCompute(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);
napi_value promise;
napi_deferred deferred;
napi_create_promise(env, &deferred, &promise);
ComputeData *cdata = new ComputeData();
cdata->deferred = deferred;
napi_get_value_double(env, args[0], &cdata->input);
napi_value workName;
napi_create_string_utf8(env, "AsyncCompute", NAPI_AUTO_LENGTH, &workName);
napi_create_async_work(env, nullptr, workName,
ComputeExecute, ComputeComplete,
cdata, &cdata->work);
napi_queue_async_work(env, cdata->work);
return promise;
}
// ========== 4. 返回对象 ==========
static napi_value GetSystemInfo(napi_env env, napi_callback_info info)
{
napi_value obj;
napi_create_object(env, &obj);
napi_value osVal;
napi_create_string_utf8(env, "HarmonyOS", NAPI_AUTO_LENGTH, &osVal);
napi_set_named_property(env, obj, "os", osVal);
napi_value versionVal;
napi_create_int32(env, 22, &versionVal);
napi_set_named_property(env, obj, "apiVersion", versionVal);
return obj;
}
// ========== 5. 接收数组 ==========
static napi_value SumArray(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);
uint32_t length = 0;
napi_get_array_length(env, args[0], &length);
double sum = 0;
for (uint32_t i = 0; i < length; i++) {
napi_value element;
napi_get_element(env, args[0], i, &element);
double val;
napi_get_value_double(env, element, &val);
sum += val;
}
napi_value result;
napi_create_double(env, sum, &result);
return result;
}
// ========== 模块注册 ==========
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
{"greet", nullptr, Greet, nullptr, nullptr, nullptr, napi_default, nullptr},
{"asyncCompute", nullptr, AsyncCompute, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getSystemInfo", nullptr, GetSystemInfo, nullptr, nullptr, nullptr, napi_default, nullptr},
{"sumArray", nullptr, SumArray, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "napidemo",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterModule(void) {
napi_module_register(&demoModule);
}
对应类型声明:
export const add: (a: number, b: number) => number;
export const greet: (name: string) => string;
export const asyncCompute: (input: number) => Promise<number>;
export const getSystemInfo: () => { os: string; apiVersion: number };
export const sumArray: (arr: number[]) => number;
10. API 速查表
类型转换
| 操作 | 函数 |
|---|---|
| int32 取值 | napi_get_value_int32(env, value, &result) |
| int32 创建 | napi_create_int32(env, 42, &result) |
| double 取值 | napi_get_value_double(env, value, &result) |
| double 创建 | napi_create_double(env, 3.14, &result) |
| bool 取值 | napi_get_value_bool(env, value, &result) |
| bool 创建 | napi_get_boolean(env, true, &result) |
| string 取值(长度) | napi_get_value_string_utf8(env, value, nullptr, 0, &len) |
| string 取值(内容) | napi_get_value_string_utf8(env, value, buf, bufSize, &len) |
| string 创建 | napi_create_string_utf8(env, "str", NAPI_AUTO_LENGTH, &result) |
对象与数组
| 操作 | 函数 |
|---|---|
| 创建对象 | napi_create_object(env, &result) |
| 设置属性 | napi_set_named_property(env, obj, "key", value) |
| 获取属性 | napi_get_named_property(env, obj, "key", &result) |
| 创建数组 | napi_create_array(env, &result) |
| 获取数组长度 | napi_get_array_length(env, arr, &length) |
| 获取数组元素 | napi_get_element(env, arr, index, &result) |
| 设置数组元素 | napi_set_element(env, arr, index, value) |
类型判断
| 操作 | 函数 |
|---|---|
| 判断类型 | napi_typeof(env, value, &type) |
| 判断数组 | napi_is_array(env, value, &result) |
| 判断 undefined | napi_is_undefined(env, value, &result) |
| 判断 null | napi_is_null(env, value, &result) |
函数与回调
| 操作 | 函数 |
|---|---|
| 调用JS函数 | napi_call_function(env, this, func, argc, args, &result) |
| 创建函数 | napi_create_function(env, "name", NAPI_AUTO_LENGTH, callback, nullptr, &result) |
异步工作
| 操作 | 函数 |
|---|---|
| 创建 Promise | napi_create_promise(env, &deferred, &promise) |
| resolve | napi_resolve_deferred(env, deferred, value) |
| reject | napi_reject_deferred(env, deferred, error) |
| 创建异步工作 | napi_create_async_work(env, nullptr, name, execute, complete, data, &work) |
| 入队执行 | napi_queue_async_work(env, work) |
| 取消 | napi_cancel_async_work(env, work) |
| 删除 | napi_delete_async_work(env, work) |
生命周期
| 操作 | 函数 |
|---|---|
| 包装原生对象 | napi_wrap(env, jsObj, nativeObj, destructor, hint, &result) |
| 取回原生对象 | napi_unwrap(env, jsObj, (void **)&nativeObj) |
| 移除包装 | napi_remove_wrap(env, jsObj, (void **)&nativeObj) |
| 定义类 | napi_define_class(env, "ClassName", NAPI_AUTO_LENGTH, constructor, nullptr, propCount, properties, &result) |
11. 调试技巧
11.1 日志输出
#include "hilog/log.h"
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x0000
#define LOG_TAG "MyModule"
// 级别:DEBUG < INFO < WARN < ERROR < FATAL
OH_LOG_DEBUG(LOG_APP, "value: %{public}d", intValue); // 明文输出
OH_LOG_INFO(LOG_APP, "name: %{private}s", strValue); // 隐私过滤 <private>
OH_LOG_ERROR(LOG_APP, "error code: %{public}d", code);
%{public}明文输出%{private}隐私过滤,显示为<private>- 缺省默认为
%{private}
11.2 状态码检查
每个 NAPI 函数都返回 napi_status,养成检查的习惯:
napi_status status = napi_get_value_double(env, args[0], &value);
if (status != napi_ok) {
OH_LOG_ERROR(LOG_APP, "napi_get_value_double failed: %{public}d", status);
napi_value undefined;
napi_get_undefined(env, &undefined);
return undefined;
}
11.3 抛出异常
napi_throw_type_error(env, nullptr, "Argument must be a number");
return nullptr;
11.4 参数数量校验
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (argc < 2) {
napi_throw_type_error(env, nullptr, "Expected 2 arguments");
return nullptr;
}
12. 常见问题
Q1: ArkTS 调用时 TypeError: xxx is not callable
原因:nm_modname 与其他 SO 库冲突,或方法名拼写与 import 调用不一致。
检查:
nm_modname是否全局唯一- NAPI 注册的方法名是否与
index.d.ts导出名一致 napi_module_register()是否被正确调用
Q2: 异步工作中访问 napi_env 的限制
规则:
Execute回调(工作线程)不能调用任何 NAPI 函数,只能操作 C++ 数据Complete回调(主线程)可以正常调用 NAPI 函数
如果 Execute 中需要日志,使用 HILOG_INFO / HILOG_ERROR。
Q3: 内存泄漏
常见泄漏场景:
new出的内存在异常路径上未deletenapi_create_async_work后未napi_delete_async_worknapi_wrap的原生对象未提供析构回调
建议:使用 RAII 模式(如 std::unique_ptr)管理 C++ 侧内存。
Q4: 字符串中文乱码
确保创建字符串时使用 UTF-8 编码:
napi_create_string_utf8(env, utf8Str, NAPI_AUTO_LENGTH, &result);
如果三方库返回的是 GBK 等其他编码,需要先在 C++ 侧转为 UTF-8。
Q5: Cannot find module ‘xxx.so’
原因:类型声明配置不完整。
检查清单:
types/xxx/oh-package.json5的 name 是否为xxx.so格式(带后缀)entry/oh-package.json5的 dependencies key 是否也为xxx.so格式- 是否执行了
ohpm install entry/oh_modules/xxx.so/index.d.ts是否存在
Q6: napi_property_descriptor 各字段含义
napi_property_descriptor desc = {
"methodName", // utf8name: 导出方法名(ArkTS调用的名字)
nullptr, // name: napi_value形式的名字(与utf8name二选一)
MethodCallback, // method: C++实现函数
nullptr, // getter: 属性获取函数
nullptr, // setter: 属性设置函数
nullptr, // value: 属性值(与方法互斥)
napi_default, // attributes: 属性特性(napi_default/napi_writable等)
nullptr // data: 传递给回调的用户数据
};
Q7: 如何返回多个值
方式一:返回对象
napi_value obj;
napi_create_object(env, &obj);
napi_value val1, val2;
napi_create_int32(env, 10, &val1);
napi_create_int32(env, 20, &val2);
napi_set_named_property(env, obj, "a", val1);
napi_set_named_property(env, obj, "b", val2);
return obj;
方式二:返回数组
napi_value arr;
napi_create_array(env, &arr);
napi_value val1, val2;
napi_create_int32(env, 10, &val1);
napi_create_int32(env, 20, &val2);
napi_set_element(env, arr, 0, val1);
napi_set_element(env, arr, 1, val2);
return arr;
更多交流学习,欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
本文对应的实战项目地址:https://atomgit.com/qq8864/LibMediaInfoDemo
更多推荐

所有评论(0)