使用Node-API實(shí)現(xiàn)跨語(yǔ)言交互,首先需要按照Node-API的機(jī)制實(shí)現(xiàn)模塊的注冊(cè)和加載等相關(guān)動(dòng)作。
ArkTS/JS側(cè):實(shí)現(xiàn)C++方法的調(diào)用。代碼比較簡(jiǎn)單,import一個(gè)對(duì)應(yīng)的so庫(kù)后,即可調(diào)用C++方法。
Native側(cè):.cpp文件,實(shí)現(xiàn)模塊的注冊(cè)。需要提供注冊(cè)lib庫(kù)的名稱,并在注冊(cè)回調(diào)方法中定義接口的映射關(guān)系,即Native方法及對(duì)應(yīng)的JS/ArkTS接口名稱等。
此處以在ArkTS/JS側(cè)實(shí)現(xiàn)add()接口、在Native側(cè)實(shí)現(xiàn)Add()接口,從而實(shí)現(xiàn)跨語(yǔ)言交互為例,呈現(xiàn)使用Node-API進(jìn)行跨語(yǔ)言交互的流程。
創(chuàng)建Native C++工程
- 在DevEco Studio中New > Create Project,選擇Native C++模板,點(diǎn)擊Next,選擇API版本,設(shè)置好工程名稱,點(diǎn)擊Finish,創(chuàng)建得到新工程。
- 創(chuàng)建工程后工程結(jié)構(gòu)可以分兩部分,cpp部分和ets部分,具體可見(jiàn)下文的工程目錄介紹。
主要工程目錄介紹
- entry > src > main > cpp > types:用于存放C++的API接口描述文件。
- entry > src > main > cpp > types > libentry > index.d.ts:描述C++ API接口行為,如接口名、入?yún)ⅰ⒎祷貐?shù)等。
- entry > src > main > cpp > types > libentry > oh-package.json5:配置.so三方包聲明文件的入口及包名。
- entry > src > main > cpp > CMakeLists.txt:C++源碼編譯配置文件,提供CMake構(gòu)建腳本。
- entry > src > main > cpp > hello.cpp:定義C++ API接口的文件。
- entry > src > main > ets:用于存放ArkTS源碼。
Native側(cè)方法的實(shí)現(xiàn)
-
設(shè)置模塊注冊(cè)信息
ArkTS側(cè)import native模塊時(shí),會(huì)加載其對(duì)應(yīng)的so。加載so時(shí),首先會(huì)調(diào)用napi_module_register方法,將模塊注冊(cè)到系統(tǒng)中,并調(diào)用模塊初始化函數(shù)。
napi_module有兩個(gè)關(guān)鍵屬性:一個(gè)是.nm_register_func,定義模塊初始化函數(shù);另一個(gè)是.nm_modname,定義模塊的名稱,也就是ArkTS側(cè)引入的so庫(kù)的名稱,模塊系統(tǒng)會(huì)根據(jù)此名稱來(lái)區(qū)分不同的so。
// entry/src/main/cpp/hello.cpp
// 準(zhǔn)備模塊加載相關(guān)信息,將上述Init函數(shù)與本模塊名等信息記錄下來(lái)。
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = nullptr,
.reserved = {0},
};
// 加載so時(shí),該函數(shù)會(huì)自動(dòng)被調(diào)用,將上述demoModule模塊注冊(cè)到系統(tǒng)中。
extern "C" __attribute__((constructor)) void RegisterDemoModule() {
napi_module_register(&demoModule);
}
-
模塊初始化
實(shí)現(xiàn)ArkTS接口與C++接口的綁定和映射。
// entry/src/main/cpp/hello.cpp
EXTERN_C_START
// 模塊初始化
static napi_value Init(napi_env env, napi_value exports) {
// ArkTS接口與C++接口的綁定和映射
napi_property_descriptor desc[] = {
{"callNative", nullptr, CallNative, nullptr, nullptr, nullptr, napi_default, nullptr},
{"nativeCallArkTS", nullptr, NativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr},
};
// 在exports對(duì)象上掛載CallNative/NativeCallArkTS兩個(gè)Native方法
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 = "entry",
.nm_priv = nullptr,
.reserved = {0},
};
- 在index.d.ts文件中,提供JS側(cè)的接口方法。
// entry/src/main/cpp/types/libentry/index.d.ts
export const callNative: (a: number, b: number) => number;
export const nativeCallArkTS: (cb: (a: number) => number) => number;
- 在oh-package.json5文件中將index.d.ts與cpp文件關(guān)聯(lián)起來(lái)。
{
"name": "libentry.so",
"types": "./index.d.ts",
"version": "",
"description": "Please describe the basic information."
}
- 在CMakeLists.txt文件中配置CMake打包參數(shù)。
# entry/src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(MyApplication2)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
# 添加名為entry的庫(kù)
add_library(entry SHARED hello.cpp)
# 構(gòu)建此可執(zhí)行文件需要鏈接的庫(kù)
target_link_libraries(entry PUBLIC libace_napi.z.so)
- 實(shí)現(xiàn)Native側(cè)的CallNative以及NativeCallArkTS接口。具體代碼如下:
// entry/src/main/cpp/hello.cpp
static napi_value CallNative(napi_env env, napi_callback_info info)
{
size_t argc = 2;
// 聲明參數(shù)數(shù)組
napi_value args[2] = {nullptr};
// 獲取傳入的參數(shù)并依次放入?yún)?shù)數(shù)組中
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// 依次獲取參數(shù)
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);
// 返回兩數(shù)相加的結(jié)果
napi_value sum;
napi_create_double(env, value0 + value1, &sum);
return sum;
}
static napi_value NativeCallArkTS(napi_env env, napi_callback_info info)
{
size_t argc = 1;
// 聲明參數(shù)數(shù)組
napi_value args[1] = {nullptr};
// 獲取傳入的參數(shù)并依次放入?yún)?shù)數(shù)組中
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
// 創(chuàng)建一個(gè)int,作為ArkTS的入?yún)? napi_value argv = nullptr;
napi_create_int32(env, 2, &argv );
// 調(diào)用傳入的callback,并將其結(jié)果返回
napi_value result = nullptr;
napi_call_function(env, nullptr, args[0], 1, &argv, &result);
return result;
}
ArkTS側(cè)調(diào)用C/C++方法實(shí)現(xiàn)
ArkTS側(cè)通過(guò)import引入Native側(cè)包含處理邏輯的so來(lái)使用C/C++的方法。
// entry/src/main/ets/pages/Index.ets
// 通過(guò)import的方式,引入Native能力。
import nativeModule from 'libentry.so'
@Entry
@Component
struct Index {
@State message: string = 'Test Node-API callNative result: ';
@State message2: string = 'Test Node-API nativeCallArkTS result: ';
build() {
Row() {
Column() {
// 第一個(gè)按鈕,調(diào)用add方法,對(duì)應(yīng)到Native側(cè)的CallNative方法,進(jìn)行兩數(shù)相加。
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message += nativeModule.callNative(2, 3);
})
// 第二個(gè)按鈕,調(diào)用nativeCallArkTS方法,對(duì)應(yīng)到Native的NativeCallArkTS,在Native調(diào)用ArkTS function。
Text(this.message2)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message2 += nativeModule.nativeCallArkTS((a: number)=> {
return a * 2;
});
})
}
.width('100%')
}
.height('100%')
}
}
Node-API的約束限制
SO命名規(guī)則
導(dǎo)入使用的模塊名和注冊(cè)時(shí)的模塊名大小寫保持一致,如模塊名為entry,則so的名字為libentry.so,napi_module中nm_modname字段應(yīng)為entry,ArkTS側(cè)使用時(shí)寫作:import xxx from 'libentry.so'。
注冊(cè)建議
- nm_register_func對(duì)應(yīng)的函數(shù)(如上述Init函數(shù))需要加上static,防止與其他so里的符號(hào)沖突;
- 模塊注冊(cè)的入口,即使用attribute((constructor))修飾的函數(shù)的函數(shù)名(如上述RegisterDemoModule函數(shù))需要確保不與其它模塊重復(fù)。
多線程限制
每個(gè)引擎實(shí)例對(duì)應(yīng)一個(gè)JS線程,實(shí)例上的對(duì)象不能跨線程操作,否則會(huì)引起應(yīng)用crash。使用時(shí)需要遵循如下原則:
- Node-API接口只能在JS線程使用。
- Native接口入?yún)nv與特定JS線程綁定只能在創(chuàng)建時(shí)的線程使用。
寫在最后
- 如果你覺(jué)得這篇內(nèi)容對(duì)你還蠻有幫助,我想邀請(qǐng)你幫我三個(gè)小忙:
- 點(diǎn)贊,轉(zhuǎn)發(fā),有你們的 『點(diǎn)贊和評(píng)論』,才是我創(chuàng)造的動(dòng)力。
- 關(guān)注小編,同時(shí)可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識(shí)。
- 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識(shí)點(diǎn),請(qǐng)移步前往小編:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu