当前位置:首页>鸿蒙APP>鸿蒙开发实战——深入浅出 NAPI (Node-API) 开发

鸿蒙开发实战——深入浅出 NAPI (Node-API) 开发

  • 2026-10-11 06:16:39
鸿蒙开发实战——深入浅出 NAPI (Node-API) 开发

在 HarmonyOS 应用开发中,ArkTS 提供了高效的 UI 构建能力,但在处理高性能计算、复用现有的 C/C++ 库或底层系统交互时,我们往往需要深入到 Native 层。NAPI(Node-API)正是连接 ArkTS 与 C++ 的那座桥梁。

本文将结合实战代码,详细介绍 NAPI 的常用函数、C++ 类绑定机制,并重点深入讲解如何在 Native 侧实现异步任务(Async Work),涵盖 Promise 和 Callback 两种常见的调用方式。

一、 NAPI 基础与常用函数

NAPI 是 Node-API 的简写,它提供了一套稳定的 ABI(二进制接口),使得我们可以用 C/C++ 编写高性能模块,并在 ArkTS 中像调用普通函数一样使用它们。

1. 获取调用信息:napi_get_cb_info

当 ArkTS 侧调用一个 Native 方法时,C++ 侧的第一步通常是“解析参数”。napi_get_cb_info 是最基础也是最重要的函数之一。

功能:它用于获取有关函数调用的详细信息,包括:

  • ArkTS 传入的参数数量(argc)。
  • 具体的参数值(argv)。
  • 调用的上下文 this 指针。
  • 接收数据的指针。

使用场景:假设你在 Native 侧编写了一个名为 getCbArgs 的函数并导出。当 ArkTS 调用 getCbArgs(arg1, arg2) 时,Native 侧可以通过以下方式获取信息:

// 伪代码示例size_t argc = 2;napi_value args[2];napi_value thisArg;napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr);

注意: 通过 napi_get_cb_info 获取的 this 是 Native 侧的 this。具体来说,它是 Native 侧导出到 ArkTS 侧的对象实例,其中包含了该对象导出的所有成员。这一点在进行混合开发时尤为重要。

2. 调用 JS 函数:napi_call_function

与获取参数相反,当 Native 侧完成计算或需要通知 ArkTS 侧时,我们需要回调 JS 函数。这时就需要使用 napi_call_function。

它允许 Native 代码直接调用 ArkTS 侧传入的函数对象,常用于实现回调(Callback)逻辑。


二、 Node-API 异步任务开发实战

在主线程(UI 线程)中执行耗时的 CPU 密集型任务(如复杂的图像处理、大规模数学运算)会导致应用卡顿甚至 ANR(应用无响应)。因此,利用 NAPI 进行异步任务(Async Work)处理是 Native 开发的重中之重。

1. 异步任务处理流程

ArkTS 调用 NAPI 进行异步任务处理,核心在于将计算逻辑放入工作线程,计算完毕后再将结果抛回主线程。

大致步骤如下:

  1. 参数解析:读取 ArkTS 传入的参数,并将 napi_value 类型转换为 Native 数据类型(如 double、char*)。
  2. 上下文封装:定义一个自定义结构体(如 CallbackData),用于在主线程(env)和工作线程之间传递数据(参数、回调函数引用、计算结果等)。
  3. 创建异步任务:使用 napi_create_async_work 创建任务。我们需要传入两个关键回调:
  • ExecuteCB:在工作线程执行,处理耗时逻辑,不能调用 NAPI JS 接口。
  • CompleteCB:在主线程执行,当任务完成后触发,用于将结果转换回 ArkTS 数据并调用回调或 Resolve Promise。
  1. 加入队列:使用 napi_queue_async_work 将任务加入系统调度队列。

2. 实战代码:斐波那契数列计算

下面我们通过一个经典的斐波那契数列(Fibonacci)计算场景,演示如何实现 Promise 和 Callback 两种异步调用方式。

核心结构体与算法

首先,我们需要定义传输数据的结构体和核心算法函数。

#include"napi/native_api.h"// 用于在线程间传递数据的上下文结构体structCallbackData {    napi_async_work asyncWork = nullptr; // 异步工作项    napi_deferred deferred = nullptr;    // 用于 Promise 的 resolve/reject    napi_ref callback = nullptr;         // 用于 Callback 的引用double args = 0;                     // 入参double result = 0;                   // 计算结果};// 耗时的斐波那契计算(CPU密集型)staticdoublefib(double n){if (n <= 1) {return n;    }return fib(n - 1) + fib(n - 2);}

场景一:Execute 回调(通用)

无论是 Promise 还是 Callback,计算逻辑是一样的,都在工作线程中运行。注意:这里纯 C++ 运算,不涉及 NAPI JS 对象操作。

staticvoidExecuteCB(napi_env env, void *data){    CallbackData *callbackData = reinterpret_cast<CallbackData*>(data);// 执行耗时计算    callbackData->result = fib(callbackData->args);}

场景二:Promise 模式实现

Promise 是现代前端开发的主流异步方式。在 Native 侧,我们需要使用 napi_resolve_deferred 或 napi_reject_deferred 来改变 Promise 状态。

完成回调 (CompletePromiseCB):

staticvoidCompletePromiseCB(napi_env env,napi_status status, void *data){    CallbackData *callbackData = reinterpret_cast<CallbackData*>(data);    napi_value result = nullptr;// 将 C++ double 结果转回 JS Number    napi_create_double(env, callbackData->result, &result);// 根据业务逻辑 Resolve 或 Rejectif (callbackData->result > 0) {        napi_resolve_deferred(env, callbackData->deferred, result);    } else {        napi_reject_deferred(env, callbackData->deferred, result);    }// 清理资源    napi_delete_async_work(env, callbackData->asyncWork);delete callbackData;    callbackData = nullptr;}

入口函数 (AsyncWork):

static napi_value AsyncWork(napi_env env, napi_callback_info info){size_t argc = 1;    napi_value args[1];    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 1. 创建 Promise    napi_value promise = nullptr;    napi_deferred deferred;    napi_create_promise(env, &deferred, &promise);// 2. 封装上下文auto callbackData = new CallbackData();    callbackData->deferred = deferred;    napi_get_value_double(env, args[0], &callbackData->args);// 3. 创建并入队异步任务    napi_value resourceName = nullptr;    napi_create_string_utf8(env, "AsyncCallbackPromise", NAPI_AUTO_LENGTH, &resourceName);    napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompletePromiseCB, callbackData, &callbackData->asyncWork);    napi_queue_async_work(env, callbackData->asyncWork);// 4. 返回 Promise 给 ArkTSreturn promise;}

场景三:Callback 模式实现

如果业务方习惯传递回调函数 (result) => {},处理逻辑稍有不同,我们需要持久化存储 callback 引用,并在完成后调用它。

完成回调 (CompleteCallbackCB):

staticvoidCompleteCallbackCB(napi_env env,napi_status status, void *data){    CallbackData *callbackData = reinterpret_cast<CallbackData*>(data);    napi_value result = nullptr;    napi_create_double(env, callbackData->result, &result);// 获取之前的回调函数引用    napi_value callback;    napi_get_reference_value(env, callbackData->callback, &callback);// 构造参数并调用 JS 函数    napi_value tsCallbackArgs[1] = {result};    napi_value callbackResult;    napi_call_function(env, nullptr, callback, 1, tsCallbackArgs, &callbackResult);// 清理引用和资源    napi_delete_reference(env, callbackData->callback);    napi_delete_async_work(env, callbackData->asyncWork);delete callbackData;    callbackData = nullptr;}

入口函数 (AsyncWorkCallback):

static napi_value AsyncWorkCallback(napi_env env, napi_callback_info info){size_t argc = 2; // 期望接收2个参数:数值, 回调函数    napi_value args[2];    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);auto asyncContext = new CallbackData();// 读取数值参数    napi_get_value_double(env, args[0], &asyncContext->args);// 创建回调函数的引用(防止被GC)    napi_create_reference(env, args[1], 1, &asyncContext->callback);    napi_value resourceName = nullptr;    napi_create_string_utf8(env, "AsyncCallback", NAPI_AUTO_LENGTH, &resourceName);// 创建任务,传入 CompleteCallbackCB    napi_create_async_work(env, nullptr, resourceName, ExecuteCB, CompleteCallbackCB, asyncContext, &asyncContext->asyncWork);    napi_queue_async_work(env, asyncContext->asyncWork);returnnullptr; // 回调模式通常返回 undefined}

3. 模块注册与初始化

最后,别忘了将这两个方法注册到模块中,以便 ArkTS 侧可以导入使用。

EXTERN_C_STARTstatic napi_value Init(napi_env env, napi_value exports){    napi_property_descriptor desc[] = {        {"asyncWork", nullptr, AsyncWork, nullptr, nullptr, nullptr, napi_default, nullptr},        {"asyncWorkCallback", nullptr, AsyncWorkCallback, nullptr, nullptr, nullptr, napi_default, nullptr}    };    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;}EXTERN_C_ENDstatic napi_module demoModule = {    .nm_version = 1,    .nm_flags = 0,    .nm_filename = nullptr,    .nm_register_func = Init,    .nm_modname = "entry1",    .nm_priv = ((void *)0),    .reserved = {0},};extern"C" __attribute__((constructor)) voidRegisterEntryModule(void){ napi_module_register(&demoModule); }

三、 总结

通过 NAPI,我们可以充分利用 C++ 的生态和性能优势。在开发过程中,异步任务的处理尤为关键:

  1. 数据隔离:使用自定义结构体 CallbackData 承载数据。
  2. 线程职责明确:计算逻辑在 Worker 线程,数据转换与 JS 交互在 Main 线程。
  3. 资源管理:务必记得删除 async_work、napi_ref 以及释放 C++ 内存(delete callbackData),防止内存泄漏。

掌握了这些,你就能在鸿蒙开发中自如地进行 ArkTS 与 Native 的混合编程了。

最新文章

随机文章