前言
此文代碼版本為 code-v3.1-Beta。
2022/05/22 更新:oh 官方提供了一個插件用來一鍵生成 NAPI 框架代碼、業(yè)務(wù)代碼框架、GN 文件等。因此你也許沒有必要看本文。
https://gitee.com/openharmony/napi_generator#https://gitee.com/openharmony/napi_generator/blob/master/docs/INSTRUCTION_ZH.md
“本文主要介紹NAPI框架代碼生成工具,它可以根據(jù)用戶指定路徑下的ts(typescript)接口文件一鍵生成NAPI框架代碼、業(yè)務(wù)代碼框架、GN文件等。在開發(fā)JS應(yīng)用與NAPI間接口時,底層框架開發(fā)者無需關(guān)注Nodejs語法、C++與JS之間的數(shù)據(jù)類型轉(zhuǎn)換等上層應(yīng)用轉(zhuǎn)換邏輯,只關(guān)注底層業(yè)務(wù)邏輯即可,專業(yè)的人做專業(yè)的事,從而可以大大提高開發(fā)效率?!?/p>
NAPI 是什么
類似于 Android 中使用的 JNI,OH 中的 N-API 定義了的由 JS/ETS 語言編寫的代碼編繹出的字節(jié)碼和 native 代碼(使用 C/C++ 編寫)交互的方式,由 Node.js N-API 框架擴展而來。
簡單來說,OH 系統(tǒng)中的 NAPI 定義了 JS 代碼和 C++ 代碼的交互方式。
JNI:Java Native Interface;
N-API:Native Application Programming Interface;

Android 中使用的 JNI(Java Native Interface):定義了由 Java 或 Kotlin 語言編寫的代碼編譯出的字節(jié)碼和 native 代碼(使用 C/C++ 編寫)交互的方式。
NAPI 設(shè)計思想
1. 方舟編譯器(ArkCompiler)將 JS 代碼編繹成 abc 文件,并執(zhí)行
方舟編譯器(ArkCompiler)中的 ArkCompiler JS Runtime 模塊中的 JS 編譯工具鏈會將 JS 代碼轉(zhuǎn)換成方舟字節(jié)碼即 abc 文件。然后方舟編譯器中的 JS Runtime 會直接運動字節(jié)碼文件,實現(xiàn)對應(yīng)的語義邏輯。其中的 JS-NAPI 模塊將實現(xiàn) JSNAPI 接口和 C++ 的交互。

2. ArkCompiler 中的 JS-NAPI 模塊調(diào)用 Native 代碼注冊的 JS 接口實現(xiàn)代碼(callback method handle)

3. Native 代碼以 property 的形式注冊 callback method handle, 并且在 callback method 中有一個參數(shù)用于存放 JS 方法傳入的參數(shù)值
如一個 js 對象對應(yīng)的 native 對象 object,native 代碼將把 object 中的方法、變量、常量構(gòu)成 [key:methodname,value:callback method handle] 的 property 形式注冊。

如:JS 的 call 接口 @ohos.telephony.call.d.ts 中對應(yīng)的方法 dial()、變量 callstate、常量 CALL_STATE_IDLE,native 代碼將為其注冊 call 的 properties:["dial",callback method]、["callstate",getter/setter callback method]、["CALL_STATE_IDLE", callback value]

4. native 代碼調(diào)用 napi 創(chuàng)建 JS 的 Promise,當(dāng) callback method 執(zhí)行完成后,通過 napi 調(diào)用 Promise.resolve(value) 方法向 JS 代碼返回結(jié)果。
NAPI 數(shù)據(jù)類型和封裝函數(shù)
napi 數(shù)據(jù)類型作為 js 數(shù)據(jù)類型和 c++ 數(shù)據(jù)類型之間的橋梁,可以和 ArkComplier 中翻譯的 js 數(shù)據(jù)類型,及 c++ 數(shù)據(jù)類型相互轉(zhuǎn)換。napi 框架提供方法實現(xiàn)它們之間的互相轉(zhuǎn)換。

可參考 Node.js v8.x 中文文檔 ,oh 中的具體實現(xiàn)在 native_api.cpp。
其中基本的數(shù)據(jù)類型,如 :
- napi_env 對應(yīng) NativeEngine 在 oh 中指 ArkComplier 中 JSNAPI 的相關(guān)上下文環(huán)境,任何 napi 數(shù)據(jù)類型和 js 數(shù)據(jù)類型的轉(zhuǎn)換都需要用到它。
- napi_value 對應(yīng) NativeValue 在 oh 中指所有 ArkComplier 可識別的 js 數(shù)據(jù)類型,它有子類 ArkNativeNumber、ArkNativeString、ArkNativeFunction 等,對應(yīng) js 中的 number、string、function 等數(shù)據(jù)類型。
- napi_callback_info 對應(yīng) NativeCallbackInfo ,指注冊 callback handle 時用來存放 js 傳入?yún)?shù)信息的數(shù)據(jù)類型。它是一個 struct。
- napi_property_descriptor 是用來存放單個 property 的數(shù)據(jù)類型。
- napi_callback 對應(yīng) NativeCallback,即前面的 callback handle,native 代碼將其注冊為對應(yīng) js 接口的回調(diào)函數(shù)。
typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info)
其中基本的轉(zhuǎn)換函數(shù),如:
- 將 c++ 轉(zhuǎn)換成 napi 的函數(shù): napi_create_string_utf8 等;
- 將 napi 轉(zhuǎn)換成 c++ 的函數(shù): napi_get_value_string_utf8 等;
- 創(chuàng)建編譯器中的 js 對象: napi_create_function 等;
- 回調(diào) js 函數(shù):napi_call_function
使用 NAPI
使用 napi 時只要完成以下幾個步驟:
1. 向系統(tǒng)注冊我們要實現(xiàn)的 js 接口對象(在 oh 中指要新增的子系統(tǒng))
分為以下幾步:
首先:
BUILD.gn 中聲明 js 接口對象 object :ohos_shared_libarary("object")
其次:
通過 napi_module_register 注冊 module
最后:
定義 napi_module ,注冊 module 中 object 的 properties
2. 注冊我們要提供的子系統(tǒng)接口的 properties
首先,定義所有的常量、變量、方法 property:
DECLARE_NAPI_FUNCTION("methodName", NativeMethod);
然后,注冊所有 properties
通過 napi_define_properties 注冊 object 的 properties
3. 實現(xiàn)所有 property 中的回調(diào)方法:
從 napi_callback_info 中取出 JS 調(diào)用時傳入的所有參數(shù),
并調(diào)用 napi 方法將其從 napi 數(shù)據(jù)類型的值 napi_value 轉(zhuǎn)換成 native 數(shù)據(jù)類型的值