2021-06-09 07:01:45
AKI是一个纯血鸿蒙APP的第三方库,能够通过极简语法糖实现JS与C/C++的跨语言调用,尤其在跨线程调用JS函数场景中,相比NAPI使用更方便且代码量更少。
AKI核心优势
极简语法糖:AKI通过JSBIND_ADDON、JSBIND_GLOBAL和JSBIND_FUNCTION等宏定义,实现一行代码完成JS与C/C++的跨语言互调,避免了NAPI中复杂的napi_property_descriptor结构体定义和模块注册流程。
跨线程调用支持:AKI在native侧通过std::thread创建子线程,并使用aki::JSBind::GetJSFunction获取JS函数句柄后,直接通过Invoke触发调用,无需手动管理libuv任务队列。
代码量显著减少:对比NAPI实现,AKI在native侧的业务函数实现更简洁,例如AKI的AkiThreadsCallJs函数仅需10余行代码即可完成跨线程调用,而NAPI的UvWorkTest和CallbackUvWorkTest需要近百行代码。
AKI实现跨线程调用JS函数的关键步骤
初始化阶段:
使用JSBIND_ADDON(aki_use_practice)注册Native插件,插件名与编译生成的.so文件名一致。
通过JSBIND_GLOBAL()圈定全局函数作用域,并在其中使用JSBIND_FUNCTION(AkiThreadsCallJs)绑定C++全局函数,使其可直接被JS调用。

Native侧业务函数实现:
在AkiThreadsCallJs中创建子线程,子线程通过aki::JSBind::GetJSFunction("akiAccumulate")获取JS函数句柄。
使用func->Invoke<void>(value, callback)调用JS函数,其中Invoke<T>指定返回值类型,此处为void。
void AkiThreadsCallJs(int value) { std::thread subThread([=]() { if (auto func = aki::JSBind::GetJSFunction("akiAccumulate")) { std::function<void(int)> callback = [](int value) {}; func->Invoke<void>(value, callback); } }); subThread.detach();}ArkTS侧调用:
使用JSBind.bindFunction绑定JS全局函数akiAccumulate,在函数内对传入值加10并更新UI。
通过libaki.AkiThreadsCallJs(this.value)调用C++全局函数,触发跨线程操作。
libaki.JSBind.bindFunction("akiAccumulate", (values: number) => { values += 10; this.value = values;});libaki.AkiThreadsCallJs(this.value);AKI与NAPI实现对比
初始化复杂度:
AKI仅需3个宏定义即可完成插件和函数注册,而NAPI需要定义napi_property_descriptor结构体,并通过napi_define_properties和napi_module_register手动注册模块。
跨线程调用实现:
AKI直接通过std::thread和Invoke实现跨线程调用,代码简洁。
NAPI需创建uv_work_t任务结构体,并通过uv_queue_work将任务添加到libuv队列,由事件循环执行,代码复杂度高。
性能与易用性权衡:
AKI虽在性能上略逊于NAPI,但其易用性显著优于NAPI,适合对性能要求不高但追求开发效率的场景。
工程结构与模块依赖
工程结构:
akiusepractice |---srcmaincpp | |---akiusepractice.cpp | |---CMakeLists.txt |---srcmainetsview | |---AkiView.ets模块依赖:
依赖AKI库提供跨语言调用能力。
依赖common模块中的FunctionDescription公共组件。
适用场景与建议
推荐使用AKI的场景:
对性能要求不高,但需要快速实现JS与C/C++跨语言调用的项目。
开发跨线程调用JS函数的场景,AKI可显著减少代码量和开发复杂度。
注意事项:
若项目对性能有极高要求,建议评估NAPI或其他原生方案。
使用AKI时需确保正确处理线程安全,避免多线程环境下的竞态条件。