往期回顧:NAPI 類對象導(dǎo)出及其生命周期管理(上)
4. 樣例工程源碼剖析
4.1. NAPI導(dǎo)出對象和生命周期管理具體實現(xiàn)
4.1.1. 定義NapiTest類及方法
- Napi.h文件內(nèi)容如下:
#ifndef __NAPI_TEST_H__
#define __NAPI_TEST_H__
#include "napi/native_api.h"
#include
#include
#define NAPI_CLASS_NAME "NapiTestClass"
class NapiTest {
public:
NapiTest() : mEnv(nullptr), mRef(nullptr) {
}
NapiTest(napi_env env) : mEnv(env), mRef(nullptr){
}
~NapiTest();
// 創(chuàng)建NapiTest類的實體,并將實體返回到應(yīng)用端,該方法為js創(chuàng)建一個類實體,因此需要將該接口對外導(dǎo)出
static napi_value Create(napi_env env, napi_callback_info info);
// 初始化js類并設(shè)置對應(yīng)屬性并將其導(dǎo)出
static napi_value Init(napi_env env, napi_value exports);
private:
// 設(shè)置數(shù)據(jù),此方法給到j(luò)s直接調(diào)用,因此需要將該接口對外導(dǎo)出
static napi_value SetMsg(napi_env env, napi_callback_info info);
// 獲取數(shù)據(jù),此方法給到j(luò)s直接調(diào)用,因此需要將該接口對外導(dǎo)出
static napi_value GetMsg(napi_env env, napi_callback_info info);
// 定義js結(jié)構(gòu)體時實際的構(gòu)建函數(shù)
static napi_value Constructor(napi_env env, napi_callback_info info);
// 釋放資源的函數(shù)(類似類的析構(gòu)函數(shù))
static void Destructor(napi_env env, void *nativeObject, void *finalize);
// 生命周期變量
static napi_ref sConstructor_;
// 設(shè)置和獲取數(shù)據(jù)的變量
static std::string _msg;
// 記錄環(huán)境變量
napi_env mEnv = nullptr;
// 記錄生命周期變量
napi_ref mRef = nullptr;
};
#endif /* __NAPI_TEST_H__ */
4.1.1.1 napi_value
-
Node.js Node-API的值用napi_value類型表示。
OpenHarmony NAPI將ECMAScript標(biāo)準(zhǔn)中定義的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八種數(shù)據(jù)類型,以及函數(shù)對應(yīng)的Function類型,統(tǒng)一封裝成napi_value類型,下文中表述為JS類型,用于接收ArkUI應(yīng)用傳遞過來的數(shù)據(jù)及返回數(shù)據(jù)給ArkUI應(yīng)用。
-
這是一個不透明的指針,用于表示JavaScript值。
4.1.1.2 napi_ref
-
這是用來引用napi_value的抽象。這允許用戶管理JavaScript值的生命周期,包括顯式地定義它們的最小生命周期。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_ref
4.1.1.3 napi_env
-
napi_env用于表示上下文,底層的Node-API實現(xiàn)可以使用該上下文持久保持VM-specific的狀態(tài)。
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_env
4.1.2 將NapiTest類定義為js類
4.1.2.1在定義js類之前,需要先設(shè)置js類對外導(dǎo)出的方法
// 在定義js類之前,需要先設(shè)置類對外導(dǎo)出的方法
napi_property_descriptor desc[] = {
{ "getMsg", nullptr, NapiTest::GetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "setMsg", nullptr, NapiTest::SetMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr }
};
4.1.2.1.1 napi_property_descriptor
參考 https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_property_descriptor
Node.js Node-API有一組API來獲取和設(shè)置JavaScript對象的屬性。在JavaScript中,屬性被表示為一個鍵和一個值的元組。基本上,Node-API中的所有屬性鍵都可以用以下形式中的任一一種表示:
- Named:一個簡單的UTF-8編碼的字符串
- Integer-Indexed:索引值,由uint32_t表示
- JavaScript value:在Node-API中通過napi_value表示。它可以是一個napi_value,表示字符串、數(shù)字或符號。
typedef struct {
// utf8name和name其中一個必須是NULL
const char* utf8name;
napi_value name;
napi_callback method;
napi_callback getter;
napi_callback setter;
napi_value value;
napi_property_attributes attributes;
void* data;
} napi_property_descriptor;
參數(shù)解析:
- utf8name:在定義js類之前設(shè)置的js類對外導(dǎo)出的方法名字,編碼為UTF8。必須為該屬性提供utf8name或name中的一個。(utf8name和name其中一個必須是NULL)
- name:可選的napi_value,指向一個JavaScript字符串或符號,用作屬性的鍵。必須為該屬性提供utf8name或name中的一個。
- method:將屬性描述符對象的value屬性設(shè)置為method表示的JavaScript函數(shù)。如果傳入這個參數(shù),將value、getter和setter設(shè)置為NULL(因為這些成員不會被使用)。
- attributes:與特定屬性相關(guān)聯(lián)的屬性。
- data:調(diào)用函數(shù)時傳遞給method、getter和setter的callback data。
4.1.2.2 定義與C++類相對應(yīng)的JavaScript類
napi_value constructor = nullptr;
// 定義與C++類相對應(yīng)的JavaScript類
if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]),
desc, &constructor) != napi_ok) {
// "!="用來檢查兩個操作數(shù)的值是否相等,如果不相等則條件為真
return nullptr;
}
4.1.2.2.1 napi_define_class
napi_define_class
函數(shù)說明:
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_class
napi_status napi_define_class(napi_env env,
const char* utf8name,
size_t length,
napi_callback constructor,
void* data,
size_t property_count,
const napi_property_descriptor* properties,
napi_value* result);
功能:定義與C ++ 類相對應(yīng)的JavaScript類。
參數(shù)說明:
-
[in] env: 調(diào)用api的環(huán)境
-
[in] utf8name: C ++ 類的名稱
-
[in] length: C ++ 類的名稱的長度,默認(rèn)自動長度使用
NAPI_AUTO_LENGTH
-
[in] constructor: 處理C ++ 類實例構(gòu)造的回調(diào)函數(shù) (因為Constructor函數(shù)被napi_define_class調(diào)用了)。在導(dǎo)出C ++ 類對象時,這個函數(shù)必須是帶有napi_callback簽名(Constructor函數(shù)有napi_callback簽名是指要滿足typedef napi_value (*napi_callback)(napi_env, napi_callback_info);的形式)的靜態(tài)成員。不能使用c ++ 的類構(gòu)造函數(shù)。
-
[in] data: 作為回調(diào)信息的數(shù)據(jù)屬性傳遞給構(gòu)造函數(shù)回調(diào)的可選數(shù)據(jù)
-
[in] property_count: 屬性數(shù)組中參數(shù)的個數(shù)
-
[in] properties: 屬性數(shù)組,具體看代碼中napi_property_descriptor部分
-
[out] result: 通過類構(gòu)造函數(shù)綁定類實例的napi_value對象
返回:如果API調(diào)用成功返回napi_ok。
JS構(gòu)造函數(shù)
如果一個js函數(shù)被使用new操作符來調(diào)用了,那么這個函數(shù)就稱之為js構(gòu)造函數(shù)
C++類回調(diào)函數(shù)
我們調(diào)用別人的API叫call,調(diào)用的第三方API調(diào)用我們的函數(shù)叫回調(diào)(callback)
4.1.2.3 實現(xiàn)js類的構(gòu)造函數(shù)
當(dāng)ArkTS應(yīng)用在js端通過new方法獲取類對象的時候,此時會調(diào)用 napi_define_class 中設(shè)置的 constructor 回調(diào)函數(shù),該函數(shù)實現(xiàn)方法如下:
napi_value NapiTest::Constructor(napi_env env, napi_callback_info info)
{
napi_value undefineVar = nullptr, thisVar = nullptr;
napi_get_undefined(env, &undefineVar);
// 獲取傳入的參數(shù)對象,對象不為空,根據(jù)該參數(shù)創(chuàng)建實例并并綁定到該對象
if (napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr) == napi_ok && thisVar != nullptr) {
// 創(chuàng)建NapiTest 實例
NapiTest *reference = new NapiTest(env);
// 綁定實例到對象并獲取對象的生命周期
if (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference), NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) {
return thisVar;
}
return thisVar;
}
return undefineVar;
}
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
// 釋放資源
NapiTest *test = reinterpret_cast
- NapiTest::Destructo方法是用來釋放創(chuàng)建的對象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize)
{
// 類析構(gòu)函數(shù),釋放資源
NapiTest *test = reinterpret_cast
4.1.2.3.1 napi_wrap
napi_status napi_wrap(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
void* finalize_hint,
napi_ref* result);
功能:將C++類實例綁定到j(luò)s對象,并關(guān)聯(lián)對應(yīng)的生命周期
參數(shù)說明:
- [in] env: 調(diào)用api的環(huán)境
- [in] js_object: 綁定native_object的js對象
- [in] native_object: C++類實例對象
- [in] finalize_cb: 釋放實例對象的回調(diào)函數(shù)
- [in] finalize_hint: 傳遞給回調(diào)函數(shù)的數(shù)據(jù)
- [out] result: 綁定js對象的引用
返回:調(diào)用成功返回0,失敗返回其他
4.1.2.3.2 napi_get_cb_info
NAPI提供了napi_get_cb_info()方法可從napi_callback_info中獲取參數(shù)列表、this及其他數(shù)據(jù)。這個方法在constructor回調(diào)函數(shù)中使用,從給定的回調(diào)信息中檢索有關(guān)調(diào)用的詳細(xì)信息,如參數(shù)和This指針。
napi_status napi_get_cb_info(napi_env env,
napi_callback_info cbinfo,
size_t* argc,
napi_value* argv,
napi_value* this_arg,
void** data)
參數(shù)說明:
- [in] env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可
- [in] cbinfo: napi_callback_info對象,上下文的信息
- [in-out] argc: argv數(shù)組的長度。若napi_callback_info中實際包含的參數(shù)的個數(shù)大于請求的數(shù)量argc,將只復(fù)制argc的值所指定數(shù)量的參數(shù)只argv中。若實際的參數(shù)個數(shù)小于請求的數(shù)量,將復(fù)制全部的參數(shù),數(shù)組多余的空間用空值填充,并將參數(shù)實際長度寫入argc。
- [out] argv: 用于接收參數(shù)列表
- [out] this_arg: 用于接收this對象
- [out] data: NAPI的上下文數(shù)據(jù) 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。下面的返回napi_status方法一樣。
4.1.3 導(dǎo)出js類
// 創(chuàng)建生命周期,初始引用計數(shù)設(shè)為1
if (napi_create_reference(env, constructor, 1, &sConstructor_) != napi_ok) {
return nullptr;
}
// 設(shè)置NapiTest對象相關(guān)屬性并綁定到導(dǎo)出變量exports
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {
return nullptr;
}
4.1.3.1 在設(shè)置js類導(dǎo)出前,需要先創(chuàng)建生命周期
if (napi_create_reference(env, constructor , 1, &sConstructor_) != napi_ok) {
return nullptr;
}
- constructor 定義js類時返回的代表類的構(gòu)造函數(shù)的數(shù)據(jù)
- sConstructor_ 生命周期變量
4.1.3.1.1 napi_create_reference
napi_create_reference
為對象創(chuàng)建一個reference,以延長其生命周期。調(diào)用者需要自己管理reference生命周期。
napi_create_reference函數(shù)說明:
NAPI_EXTERN napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result);
功能:通過引用對象創(chuàng)建新的生命周期引用對象
-
[in] env: 調(diào)用 API 的環(huán)境
-
[in] value: napi_value表示我們要引用的對象
-
[in] initial_refcount: 生命周期變量的初始引用計數(shù)
-
[out] result: 新建的生命周期引用對象
返回 napi_ok 這個API就是成功的.
4.1.3.2 將生命周期變量作為導(dǎo)出對象的傳入屬性,并將js類導(dǎo)出到exports中
// 設(shè)置constructor對象相關(guān)屬性并綁定到導(dǎo)出變量exports
if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) {
return nullptr;
}
4.1.3.2.1 napi_set_named_property
為給定對象的屬性設(shè)置一個名稱。
napi_status napi_set_named_property(napi_env env,
napi_value object,
const char* utf8Name,
napi_value value);
-
[in] env: 調(diào)用API的環(huán)境
-
[in] object: NapiTest對象相關(guān)屬性要綁定的屬性值
-
[in] utf8Name: js類的名稱
-
[in] value: 要引用的對象
返回 napi_ok 則這個API是成功的
4.1.3.3 設(shè)置導(dǎo)出對象的屬性
hello.cpp中
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
4.1.3.3.1 napi_define_properties
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_define_properties
napi_status napi_define_properties(napi_env env,
napi_value object,
size_t property_count,
const napi_property_descriptor* properties);
作用:批量的向給定Object中定義屬性
- [in] env: 調(diào)用api的環(huán)境
- [in] object: js對象相關(guān)屬性的導(dǎo)出變量
- [in] property_count: 屬性數(shù)組中的元素數(shù)
- [in] properties: 屬性數(shù)組
4.1.4 創(chuàng)建類的實例對象
- ArkTS應(yīng)用除了調(diào)用new方法獲取類的實例外,我們也可以提供一些方法讓ArkTS應(yīng)用獲取對應(yīng)的類的實例,如在我們的NapiTest類中,定義了一個Create方法,該方法實現(xiàn)了NapiTest類實例的獲取。具體實現(xiàn)如下:
napi_value NapiTest::Create(napi_env env, napi_callback_info info) {
napi_status status;
napi_value constructor = nullptr, result = nullptr;
// 獲取生命周期變量
status = napi_get_reference_value(env, sConstructor_, &constructor);
// 創(chuàng)建生命周期內(nèi)的實例對象并將其返回
status = napi_new_instance(env, constructor, 0, nullptr, &result);
auto napiTest = new NapiTest();
// 綁定實例類創(chuàng)建NapiTest到導(dǎo)出的對象result
if (napi_wrap(env, result, reinterpret_cast<void *>(napiTest), Destructor,
nullptr, &(napiTest->mRef)) == napi_ok) {
return result;
}
return nullptr;
}
-
在napi接口的注冊中將該方法以接口的方式導(dǎo)出,應(yīng)用層就可以直接調(diào)用該接口并獲取到該類的實例對。
特別說明:如果單獨實現(xiàn)了一個類實例獲取的方法,那么js的類構(gòu)造函數(shù)可以不實現(xiàn)(也就是
定義js結(jié)構(gòu)體時實際的構(gòu)建函數(shù)
Constructor及釋放資源的函數(shù)
Destructor的代碼夠可以不寫)
4.1.4.1 napi_get_reference_value
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_get_reference_value
函數(shù)說明:
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
napi_ref ref,
napi_value* result);
- 作用:獲取與reference相關(guān)聯(lián)的js對象
- [in] env: 調(diào)用API的環(huán)境
- [in] ref: 生命周期管理的變量
- [out] result: 對象引用的reference.
4.1.4.2 napi_new_instance
https://nodejs.org/docs/latest-v14.x/api/n-api.html#n_api_napi_new_instance
napi_status napi_new_instance(napi_env env,
napi_value cons,
size_t argc,
napi_value* argv,
napi_value* result)
- 作用:通過給定的構(gòu)造函數(shù),構(gòu)建一個對象
- [in] env: 調(diào)用API的環(huán)境
- [in] cons: napi_value表示要作為構(gòu)造函數(shù)調(diào)用的 JavaScript 函數(shù)
- [in] argc: argv 數(shù)組中的元素計數(shù)
- [in] argv: JavaScript 值數(shù)組,表示構(gòu)造函數(shù)的參數(shù)napi_value。
- [out] result: napi_value表示返回的 JavaScript 對象
4.2 index.d.ts聲明文件編寫
使用NAPI框架代碼生成工具,可以根據(jù).h生成.d.ts
https://gitee.com/openharmony/napi_generator/blob/master/docs/INSTRUCTION_ZH.md
export const create : () => NapiTest;
export class NapiTest {
setMsg(msg: string): void;
getMsg(): string;
}
也可以寫成
export class NapiTest {
create();
setMsg(msg: string): void;
getMsg(): string;
}
4.3 CMakeLists.txt文件
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(ObjectWrapTest)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
# 頭文件路徑
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
# 動態(tài)庫源文件
add_library(entry SHARED hello.cpp NapiTest.cpp)
# 依賴libace_napi.z.so動態(tài)庫
target_link_libraries(entry PUBLIC libace_napi.z.so )
4.4 index.ets文件
// 讓IDE不檢查文件語法
// @ts-nocheck
import testNapi from "libentry.so";
@Entry
@Component
struct Index {
@State message: string = '導(dǎo)出對象'
@State nativePointer:number = 0
// 創(chuàng)建對象tt
tt = testNapi.create();
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
console.info("[NapiTest] Test NAPI 2 + 3 = " + testNapi.add(2, 3));
try{
if (this.nativePointer == 0) {
// log打印,在程序中添加 log
console.info("[NapiTest] Test NAPI add(2, 3) 1");
this.nativePointer = testNapi.add(2, 3)
console.info("[NapiTest] Test NAPI add(2, 3) 2");
this.tt.setMsg("2+3")
console.info("[NapiTest] Test NAPI add(2, 3) 3");
} else {
console.info("[NapiTest] Test NAPI add(0, 0) 1");
this.nativePointer = testNapi.add(0, 0)
console.info("[NapiTest] Test NAPI add(0, 0) 2");
this.tt.setMsg("4+5")
console.info("[NapiTest] Test NAPI add(0, 0) 3");
}
} catch(e) {
console.info("[NapiTest]Test NAPI error" + JSON.stringify(e));
}
console.info("[NapiTest]Test NAPI " + this.tt.getMsg() + " = " + this.nativePointer);
})
}
.width('100%')
}
.height('100%')
}
}
知識點附送
napi接口名稱
https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/native-lib/third_party_napi/napi.md
NAPI框架api調(diào)用值得借鑒的示例工程
https://gitee.com/jiangtao92/node-api-test-case
-
源碼
+關(guān)注
關(guān)注
8文章
639瀏覽量
29185 -
類對象
+關(guān)注
關(guān)注
0文章
3瀏覽量
1452 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3713瀏覽量
16254
發(fā)布評論請先 登錄
相關(guān)推薦
評論