Skip to content

ArkTS 与 C++ 交互(NAPI)

理解 NAPI(Node-API)在鸿蒙中的应用,如何通过 C++ 扩展 ArkTS 能力,以及 .so 库的集成方式。


1. NAPI 概述

NAPI(Node-API,原名 NAN) 是华为在 ArkTS 中提供的 C++ 桥接机制,允许 ArkTS 代码调用 C++ 编写的原生库(.so),实现底层能力扩展。

核心用途

  1. 高性能计算:加密、图像处理、音视频编解码
  2. 硬件驱动:传感器、蓝牙、串口通信
  3. 复用已有 C++ 库:开源库直接调用
  4. 系统级能力:绕过 ArkTS 限制的能力

架构

ArkTS 应用层
    ↓ (NAPI 桥接)
C++ 原生层 (.so)

系统底层 (内核/硬件)

2. C++ 端:导出接口

2.1 基本导出

cpp
// native/my_module.cpp
#include <napi/native_api.h>

// 要被 ArkTS 调用的 C++ 函数
int add(int a, int b) {
    return a + b;
}

// NAPI 导出函数
static napi_value Add(napi_env env, napi_callback_info info) {
    // 1. 获取参数
    size_t argc = 2;
    napi_value args[2];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    int64_t a, b;
    napi_get_value_int64(env, args[0], &a);
    napi_get_value_int64(env, args[1], &b);

    // 2. 调用 C++ 函数
    int result = add(a, b);

    // 3. 返回结果
    napi_value result_value;
    napi_create_int64(env, result, &result_value);
    return result_value;
}

// 模块导出
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor props[] = {
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, 1, props);
    return exports;
}

NAPI_MODULE(my_module, Init)

3. ArkTS 端:调用 C++ 接口

3.1 加载 .so 库

typescript
// 方式1:直接加载(推荐)
import { add } from 'libmy_module.so'

// 方式2:动态加载
const lib = require('libmy_module.so')
const add = lib.add

3.2 使用 C++ 函数

typescript
import { add, multiply, sqrt } from 'libmy_module.so'

// 直接使用
let sum = add(10, 20)       // 30
let product = multiply(5, 6) // 30
let root = sqrt(144)         // 12

4. napi_ref 机制

4.1 什么是 napi_ref?

napi_ref 是 C++ 侧对 ArkTS 对象的强引用,防止对象被 GC(垃圾回收)回收。

cpp
// C++ 侧保存对 ArkTS 对象的引用
napi_ref callbackRef;

static void SaveCallback(napi_env env, napi_value callback) {
    napi_create_reference(env, callback, 1, &callbackRef);  // 1 = 强引用
}

static void InvokeCallback(napi_env env) {
    napi_value callback;
    napi_get_reference_value(env, callbackRef, &callback);
    napi_call_function(env, nullptr, callback, 0, nullptr, nullptr);
    // 使用完后释放
    napi_delete_reference(env, callbackRef);
}

4.2 napi_ref 的使用场景

场景是否需要 napi_ref
保存回调函数供后续调用✅ 需要,防止 GC
临时参数传递❌ 不需要
异步操作结果回调✅ 需要
事件监听回调✅ 需要

5. NAPI 数据类型映射

ArkTS 类型NAPI C++ 类型获取函数
numbernapi_numbernapi_get\_value\_int64
stringnapi_stringnapi\_get\_value\_string
booleannapi_booleannapi\_get\_value\_boolean
objectnapi_objectnapi\_get\_property_names
functionnapi_functionnapi\_get\_cb\_info
null/undefinednapi_null/napi_undefinednapi\_get\_null/undefined
bigintnapi_bigintnapi\_get\_value\_big\_int64

6. 实战示例:加密模块

6.1 C++ 端实现

cpp
#include <napi/native_api.h>
#include <openssl/sha.h>

static napi_value Sha256(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);

    size_t dataLen;
    char* data;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &dataLen);
    data = new char[dataLen + 1];
    napi_get_value_string_utf8(env, args[0], data, dataLen + 1, &dataLen);

    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256((unsigned char*)data, dataLen, hash);
    delete[] data;

    // 返回 hex 字符串
    char hex[SHA256_DIGEST_LENGTH * 2 + 1];
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        sprintf(&hex[i * 2], "%02x", hash[i]);
    }

    napi_value result;
    napi_create_string_utf8(env, hex, NAPI_AUTO_LENGTH, &result);
    return result;
}

static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor props[] = {
        {"sha256", nullptr, Sha256, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, 1, props);
    return exports;
}

NAPI_MODULE(crypto_module, Init)

6.2 ArkTS 端使用

typescript
import { sha256 } from 'libcrypto_module.so'

// 加密敏感数据
const password = 'mySecretPassword'
const hash = sha256(password)
console.log('加密后:', hash)  // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

7. 构建与集成

7.1 build-profile.json5 配置

json5
{
  "module": {
    "ndk": {
      "cppFlags": "-std=c++17",
      "libPaths": ["src/main/cpp/libs"]
    },
    "externalNativeOptions": {
      "path": "src/main/cpp/CMakeLists.txt",
      "arguments": "",
      "cppFlags": "",
      "abiFilters": ["arm64-v8a", "x86_64"]
    }
  }
}

7.2 CMakeLists.txt

cmake
cmake_minimum_required(VERSION 3.13)
project(MyNativeModule)

add_library(my_module SHARED
    src/main/cpp/native/my_module.cpp
)

find_library(
    napi_lib
    native
)

target_link_libraries(my_module PUBLIC ${napi_lib})

8. 面试高频考点

Q1: NAPI 的作用是什么?

回答:NAPI(Node-API)是 ArkTS 与 C++ 之间的桥接机制,允许 ArkTS 调用 C++ 编写的原生库,实现高性能计算、硬件驱动和已有 C++ 库复用。

Q2: napi_ref 的作用?

回答:napi_ref 是 C++ 侧对 ArkTS 对象的强引用,防止对象被 GC 回收。主要用于保存回调函数供异步操作后续调用。

Q3: NAPI 相比 JNI 有什么区别?

回答:NAPI 是鸿蒙平台专用的 API,更轻量、更高效;JNI 是 Android 平台的概念。两者原理类似,都是 Java/C++ 桥接。


🐱 小猫提示:NAPI 是高级开发的加分项。面试中不需要写 C++ 代码,但要理解"桥接机制"和"napi_ref 防 GC"这两个核心概念。