
鸿蒙5跨语言调用:C++/JS互操作实战指南
一、ArkCompiler跨语言能力概述
鸿蒙5的ArkCompiler为多语言互操作提供了统一的基础设施,特别是对C++和JavaScript的互操作进行了深度优化。主要特性包括:
高性能绑定:通过N-API实现高效跨语言调用
自动内存管理:跨语言对象生命周期自动跟踪
类型安全转换:基础类型和复杂对象的双向转换
异步调用支持:跨语言Promise支持
二、开发环境配置
- 工程配置
在build-profile.json5中启用Native能力:
{
“buildOption”: {
“napiBuild”: true,
“externalNativeOptions”: {
“path”: “./src/main/cpp/CMakeLists.txt”
}
}
}
2. CMake配置
src/main/cpp/CMakeLists.txt基础配置:
cmake_minimum_required(VERSION 3.4.1)
project(HarmonyNapi)
set(NATIVEROOT ${CMAKE_CURRENT_SOURCE_DIR})
查找鸿蒙NDK
find_package(NAPI REQUIRED)
添加Native模块
add_library(native_module SHARED
native_module.cpp
)
链接NAPI库
target_link_libraries(native_module PUBLIC NAPI::napi)
三、C++调用JS实战
- 基础类型传递
native_module.cpp:
#include <napi/native_api.h>
#include <hilog/log.h>
// C++调用JS函数示例
napi_value CallJsFunction(napi_env env, napi_callback_info info) {
// 获取JS上下文
size_t argc = 1;
napi_value args[1];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 获取全局对象
napi_value global;
napi_get_global(env, &global);
// 获取JS函数
napi_value jsFunc;
napi_get_named_property(env, global, "jsCallback", &jsFunc);
// 准备参数
napi_value argv[1];
napi_create_string_utf8(env, "Hello from C++", NAPI_AUTO_LENGTH, &argv[0]);
// 调用JS函数
napi_value result;
napi_call_function(env, global, jsFunc, 1, argv, &result);
// 处理返回值
char buf[128];
size_t len;
napi_get_value_string_utf8(env, result, buf, sizeof(buf), &len);
OH_LOG_Print(LOG_APP, LOG_INFO, 0, "NativeModule", "JS返回: %{public}s", buf);
return nullptr;
}
2. 注册Native方法
// 方法描述结构体
static napi_property_descriptor nativeMethods[] = {
{“callJsFunction”, nullptr, CallJsFunction, nullptr, nullptr, nullptr, napi_default, nullptr}
};
// 模块初始化
static napi_value Init(napi_env env, napi_value exports) {
napi_define_properties(env, exports,
sizeof(nativeMethods) / sizeof(nativeMethods[0]),
nativeMethods);
return exports;
}
// 模块注册
NAPI_MODULE(native_module, Init)
四、JS调用C++实战
- 同步方法调用
native_module.cpp:
// 加法函数示例
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);
// 参数解析
double a, b;
napi_get_value_double(env, args[0], &a);
napi_get_value_double(env, args[1], &b);
// 计算结果
double sum = a + b;
// 返回结果
napi_value result;
napi_create_double(env, sum, &result);
return result;
}
2. 异步方法调用
// 异步工作结构体
struct AsyncWorkData {
napi_async_work work;
napi_deferred deferred;
int input;
int result;
};
// 异步工作执行函数
void ExecuteWork(napi_env env, void* data) {
AsyncWorkData* workData = static_cast<AsyncWorkData*>(data);
// 模拟耗时计算
workData->result = workData->input * 2;
usleep(500000); // 500ms延迟
}
// 异步工作完成回调
void CompleteWork(napi_env env, napi_status status, void* data) {
AsyncWorkData* workData = static_cast<AsyncWorkData*>(data);
// 创建返回结果
napi_value result;
napi_create_int32(env, workData->result, &result);
// 解析Promise
napi_resolve_deferred(env, workData->deferred, result);
// 清理工作
napi_delete_async_work(env, workData->work);
delete workData;
}
// 异步函数示例
napi_value AsyncCompute(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);
// 解析参数
int input;
napi_get_value_int32(env, args[0], &input);
// 创建Promise
napi_value promise;
napi_deferred deferred;
napi_create_promise(env, &deferred, &promise);
// 准备异步工作
AsyncWorkData* workData = new AsyncWorkData();
workData->input = input;
workData->deferred = deferred;
// 创建异步工作
napi_create_async_work(env, nullptr,
napi_create_string_utf8(env, "AsyncWork", NAPI_AUTO_LENGTH),
ExecuteWork,
CompleteWork,
workData,
&workData->work);
// 排队执行工作
napi_queue_async_work(env, workData->work);
return promise;
}
五、复杂对象传递
-
对象转换工具类
// 对象转换工具
class ObjectConverter {
public:
static napi_value ConvertToNapi(napi_env env, const std::map<std::string, std::string>& map) {
napi_value obj;
napi_create_object(env, &obj);for (const auto& pair : map) { napi_value value; napi_create_string_utf8(env, pair.second.c_str(), pair.second.length(), &value); napi_set_named_property(env, obj, pair.first.c_str(), value); } return obj;
}
static std::map<std::string, std::string> ConvertFromNapi(napi_env env, napi_value obj) {
std::map<std::string, std::string> map;napi_value keys; napi_get_property_names(env, obj, &keys); uint32_t length; napi_get_array_length(env, keys, &length); for (uint32_t i = 0; i < length; i++) { napi_value key, value; napi_get_element(env, keys, i, &key); char keyBuf[256]; size_t keyLen; napi_get_value_string_utf8(env, key, keyBuf, sizeof(keyBuf), &keyLen); napi_get_named_property(env, obj, keyBuf, &value); char valueBuf[1024]; size_t valueLen; napi_get_value_string_utf8(env, value, valueBuf, sizeof(valueBuf), &valueLen); map[keyBuf] = valueBuf; } return map;
}
}; -
复杂对象传递示例
// 处理复杂对象
napi_value ProcessObject(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);// 从JS对象转换为C++ map
auto inputMap = ObjectConverter::ConvertFromNapi(env, args[0]);// 处理数据
std::map<std::string, std::string> outputMap;
for (const auto& pair : inputMap) {
outputMap[“processed_” + pair.first] = “C++_” + pair.second;
}// 转换回JS对象
return ObjectConverter::ConvertToNapi(env, outputMap);
}
六、线程安全与异步通信 -
线程安全回调
// 线程安全回调示例
void SafeCallback(napi_env env, napi_value jsCallback, void* context, void* data) {
// 参数转换
int* result = static_cast<int*>(data);// 创建回调参数
napi_value argv[1];
napi_create_int32(env, *result, &argv[0]);// 调用JS回调
napi_value global;
napi_get_global(env, &global);napi_value callbackResult;
napi_call_function(env, global, jsCallback, 1, argv, &callbackResult);delete result;
}
napi_value StartThread(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);
// 创建线程安全函数
napi_threadsafe_function tsfn;
napi_create_threadsafe_function(env, args[0], nullptr,
napi_create_string_utf8(env, "ThreadSafeFunc", NAPI_AUTO_LENGTH),
0, 1, nullptr, nullptr, nullptr,
SafeCallback, &tsfn);
// 启动工作线程
std::thread([tsfn]() {
// 模拟工作
int* result = new int(42);
// 调用线程安全函数
napi_call_threadsafe_function(tsfn, result, napi_tsfn_blocking);
// 释放线程安全函数
napi_release_threadsafe_function(tsfn, napi_tsfn_release);
}).detach();
return nullptr;
}
七、性能优化技巧
减少跨语言调用:
// 不好: 多次跨语言调用
for (int i = 0; i < 100; i++) {
napi_call_function(…);
}
// 好: 批量处理
std::vector<int> results;
for (int i = 0; i < 100; i++) {
results.push_back(compute(i));
}
napi_call_function(…); // 一次调用传递所有结果
使用TypedArray处理大数据:
napi_value ProcessBuffer(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);
// 获取TypedArray数据
void* data;
size_t length;
napi_get_typedarray_info(env, args[0], nullptr, &length, &data, nullptr, nullptr);
// 处理数据...
return nullptr;
}
对象缓存:
// 缓存常用JS对象
static napi_value cachedObject = nullptr;
napi_value GetCachedObject(napi_env env, napi_callback_info info) {
if (cachedObject == nullptr) {
napi_create_object(env, &cachedObject);
// 初始化对象属性…
}
return cachedObject;
}
八、调试与错误处理
-
错误处理示例
napi_value SafeDivide(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, “需要2个参数”);
return nullptr;
}double a, b;
napi_status status;status = napi_get_value_double(env, args[0], &a);
if (status != napi_ok) {
napi_throw_type_error(env, nullptr, “第一个参数必须是数字”);
return nullptr;
}status = napi_get_value_double(env, args[1], &b);
if (status != napi_ok) {
napi_throw_type_error(env, nullptr, “第二个参数必须是数字”);
return nullptr;
}if (b == 0) {
napi_throw_range_error(env, nullptr, “除数不能为零”);
return nullptr;
}napi_value result;
napi_create_double(env, a / b, &result);
return result;
} -
调试日志
#define NATIVE_LOG_TAG “NativeModule”
void DebugLog(const char* format, …) {
va_list args;
va_start(args, format);
OH_LOG_Print(LOG_APP, LOG_DEBUG, 0, NATIVE_LOG_TAG, format, args);
va_end(args);
}
// 使用示例
DebugLog(“函数调用参数: a=%f, b=%f”, a, b);
九、实战案例:图像处理模块
- C++图像处理核心
#include <opencv2/opencv.hpp>
napi_value ProcessImage(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);
// 获取图像缓冲区
void* buffer;
size_t bufferLength;
napi_get_buffer_info(env, args[0], &buffer, &bufferLength);
// 使用OpenCV处理图像
cv::Mat input(1080, 1920, CV_8UC4, buffer); // 假设已知尺寸
cv::Mat output;
cv::cvtColor(input, output, cv::COLOR_RGBA2GRAY);
// 创建返回缓冲区
void* resultBuffer;
napi_value result;
napi_create_buffer_copy(env, output.total() * output.elemSize(),
output.data, &resultBuffer, &result);
return result;
}
2. JS调用示例
import native from ‘libnative.so’;
function processImage(imageData) {
// 将ImageData转换为缓冲区
const buffer = imageData.data.buffer;
// 调用Native处理
const processed = native.processImage(buffer);
// 创建新的ImageData
return new ImageData(
new Uint8ClampedArray(processed),
imageData.width,
imageData.height
);
}
十、总结与最佳实践
- 设计原则
最小化跨语言调用:批量处理数据,减少调用次数
明确所有权:清晰定义跨语言对象的内存管理责任
类型安全:严格验证传入参数的类型
错误处理:提供有意义的错误信息和错误码 - 性能关键路径
对性能敏感的操作尽量在C++侧完成
大数据传输使用TypedArray或共享内存
避免在循环中进行跨语言调用 - 调试建议
使用OH_LOG_Print输出详细日志
在JS侧使用try-catch捕获Native异常
使用DevEco Studio的Native调试功能
鸿蒙5的C++/JS互操作能力为开发者提供了强大的跨语言开发能力,结合ArkCompiler的优化,可以实现接近原生性能的混合编程。通过合理的设计和优化,可以构建出高性能、可维护的跨语言应用模块。
