在鸿蒙应用开发中,经常会遇到需要复用已有 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 调用不一致。

检查

  1. nm_modname 是否全局唯一
  2. NAPI 注册的方法名是否与 index.d.ts 导出名一致
  3. napi_module_register() 是否被正确调用

Q2: 异步工作中访问 napi_env 的限制

规则

  • Execute 回调(工作线程)不能调用任何 NAPI 函数,只能操作 C++ 数据
  • Complete 回调(主线程)可以正常调用 NAPI 函数

如果 Execute 中需要日志,使用 HILOG_INFO / HILOG_ERROR

Q3: 内存泄漏

常见泄漏场景:

  1. new 出的内存在异常路径上未 delete
  2. napi_create_async_work 后未 napi_delete_async_work
  3. napi_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’

原因:类型声明配置不完整。

检查清单

  1. types/xxx/oh-package.json5 的 name 是否为 xxx.so 格式(带后缀)
  2. entry/oh-package.json5 的 dependencies key 是否也为 xxx.so 格式
  3. 是否执行了 ohpm install
  4. 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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐