簡介
js調(diào)用napi的數(shù)據(jù),對于簡單的數(shù)據(jù)類型,只需要napi返回對應(yīng)類型的napi_value數(shù)據(jù)即可 (詳情參照napi數(shù)據(jù)類型類型與同步調(diào)用)。但是對于一些復(fù)雜的數(shù)據(jù)類型(如我們常用C++的類對象),是不能直接返回一個napi_value數(shù)據(jù)的。這時我們需要對這些數(shù)據(jù)進行一系列操作后將其導(dǎo)出,這樣js才能使用導(dǎo)出后的對象。
本文以導(dǎo)出類對象為例來說明napi導(dǎo)出對象的具體過程。
類對象導(dǎo)出的具體過程:
NAPI導(dǎo)出類對象具體實現(xiàn)
這里我們以導(dǎo)出NapiTest類為例說明導(dǎo)出一個類的實現(xiàn)過程
定義NapiTest類以及相關(guān)方法
NapiTest類主要實現(xiàn)了接收js設(shè)置的數(shù)據(jù)并將該數(shù)據(jù)返回到j(luò)s應(yīng)用中,具體定義如下(NapiTest.h):
class NapiTest {
public:
NapiTest() : mEnv(nullptr), mRef(nullptr) {
}
~NapiTest();
static napi_value Create(napi_env env, napi_callback_info info); // 創(chuàng)建NapiTest類的實體,并將實體返回到應(yīng)用端,該方法為js創(chuàng)建一個類實體,因此需要將該接口對外導(dǎo)出
static napi_value Init(napi_env env, napi_value exports); // 初始化js類并設(shè)置對應(yīng)屬性并將其導(dǎo)出。
private:
static napi_value SetMsg(napi_env env, napi_callback_info info); // 設(shè)置數(shù)據(jù),此方法給到j(luò)s直接調(diào)用,因此需要將該接口對外導(dǎo)出
static napi_value GetMsg(napi_env env, napi_callback_info info); // 獲取數(shù)據(jù),此方法給到j(luò)s直接調(diào)用,因此需要將該接口對外導(dǎo)出
static napi_value Constructor(napi_env env, napi_callback_info info); // 定義js結(jié)構(gòu)體時實際的構(gòu)建函數(shù)
static void Destructor(napi_env env, void *nativeObject, void *finalize); // 釋放資源的函數(shù)(類似類的析構(gòu)函數(shù))
static napi_ref sConstructor_; // 生命周期變量
static std::string _msg; // 設(shè)置和獲取數(shù)據(jù)的變量
napi_env mEnv = nullptr; // 記錄環(huán)境變量
napi_ref mRef = nullptr; // 記錄生命周期變量
};
將NapiTest定義為js類
-
在定義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 }, }
-
定義js類
napi_value mConstructor = nullptr; if (napi_define_class(env, NAPI_CLASS_NAME, NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]), desc, &mConstructor) != napi_ok) { return nullptr; }
使用到函數(shù)說明:
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++類定義為js的類
參數(shù)說明:- [in] env: 調(diào)用api的環(huán)境
- [in] utf8name: C++類的名字
- [in] length: C++類名字的長度,默認自動長度使用NAPI_AUTO_LENGTH
- [in] constructor: 處理構(gòu)造類實例的回調(diào)函數(shù)
- [in] data: 作為回調(diào)信息的數(shù)據(jù)屬性傳遞給構(gòu)造函數(shù)回調(diào)的可選數(shù)據(jù)
- [in] property_count: 屬性數(shù)組參數(shù)中的個數(shù)
- [in] properties: 屬性數(shù)組
- [out] result: 通過類構(gòu)造函數(shù)綁定類實例的napi_value對象
返回:調(diào)用成功返回0,失敗返回其他
-
實現(xiàn)js類的構(gòu)造函數(shù)
當js應(yīng)用通過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); if (napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr) == napi_ok && thisVar != nullptr) { // 創(chuàng)建NapiTest 實例 NapiTest *reference = new NapiTest(env); // 綁定實例類創(chuàng)建NapiTest到導(dǎo)出的對象result if (napi_wrap(env, thisVar, reinterpret_cast<void *>(reference), NapiTest::Destructor, nullptr, &(reference->mRef)) == napi_ok) { return thisVar; } return thisVar; } return undefineVar; }
其中NapiTest::Destructo方法是用來釋放創(chuàng)建的對象:
void NapiTest::Destructor(napi_env env, void *nativeObject, void *finalize) { NapiTest *test = reinterpret_cast<NapiTest*>(nativeObject); test->~NapiTest(); }
使用到函數(shù)說明:
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: 綁定C++類實例的js對象
- [in] native_object: 類實例對象
- [in] finalize_cb: 釋放實例對象的回調(diào)函數(shù)
- [in] finalize_hint: 傳遞給回調(diào)函數(shù)的數(shù)據(jù)
- [out] result: 綁定js對象的引用
返回:調(diào)用成功返回0,失敗返回其他
導(dǎo)出js類
-
創(chuàng)建生命周期(生命周期相關(guān)可以參考文檔napi生命周期)
在設(shè)置類導(dǎo)出前,需要先創(chuàng)建生命周期if (napi_create_reference(env, mConstructor , 1, &sConstructor_) != napi_ok) { return nullptr; }
mConstructor 定義js類時返回的代表類的構(gòu)造函數(shù)的數(shù)據(jù)
sConstructor_ 生命周期變量 -
將類導(dǎo)出到exports中
將類以屬性值的方式導(dǎo)出if (napi_set_named_property(env, exports, NAPI_CLASS_NAME, constructor) != napi_ok) { return nullptr; }
通過以上步驟,我們基本實現(xiàn)了NapiTest這個類的導(dǎo)出。
注意:以上實現(xiàn)都是在類的Init方法中,我們只需要在NAPI注冊的接口中調(diào)用該Init即可。完整代碼可以查看NapiTest源碼
創(chuàng)建類的實例對象
js應(yīng)用除了調(diào)用new方法獲取類的實例外,我們也可以提供一些方法讓js應(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)。
實現(xiàn)NAPI接口的注冊
我們已helloworld為列,
-
新建一個hello.cpp,定義模塊
static napi_module demoModule = { .nm_version =1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "hello", .nm_priv = ((void*)0), .reserved = { 0 }, };
-
實現(xiàn)模塊的Init
EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { { "create", nullptr, NapiTest::Create, nullptr, nullptr, nullptr, napi_default, nullptr } // 單獨導(dǎo)出 create 方法,js應(yīng)用可以直接調(diào)用Create方法獲取類實例 }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return NapiTest::Init(env, exports); // 導(dǎo)出類以及類的方法 } EXTERN_C_END
-
模塊注冊
// 注冊 hello模塊 extern "C" __attribute__((constructor)) void RegisterHelloModule(void) { napi_module_register(&demoModule); }
至此,我們完成了整個napi接口注冊以及napi類的導(dǎo)出。
應(yīng)用調(diào)用NAPI實例
導(dǎo)出接口
在使用該NAPI的時候,我們需要在ts文件(路徑在\entry\src\main\cpp\types\libentry\index.d.ts),聲明以下內(nèi)容:
export const create : () => NapiTest;
export class NapiTest {
setMsg(msg: string): void;
getMsg(): string;
}
該文件申明了NAPI接口中導(dǎo)出的方法和類
應(yīng)用調(diào)用
新建一個helloworld的ETS工程,該工程中包含一個按鍵,我們可以通過該按鍵進行數(shù)據(jù)的在native C++中存儲和獲取
-
導(dǎo)出napi對應(yīng)的庫(之前NAPI接口生成的庫名為libentry.so)
import testNapi from "libentry.so";
-
定義變量 tt
struct Index { @State message: string = 'Hello World' @State flag:number = 0 tt = testNapi.create(); build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) .onClick(() => { }) } .width('100%') } .height('100%') }
-
在按鍵中調(diào)用對應(yīng)的接口并輸出內(nèi)容
if (this.falg == 0) { this.flag = 2 this.tt.setMsg("1+1") } else { this.flag = 0 this.tt.setMsg("1-1") } console.info("[NapiTest]:" + this.tt.getMsg() + " = " + this.flag);
通過IDE LOG信息可以查看到,當按多次下按鈕時,出現(xiàn)交替以下信息:
02200/JsApp: [NapiTest]: 1+1 = 2 02200/JsApp: [NapiTest]: 1-1 = 0
寫在最后
如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個小忙:
- 點贊,轉(zhuǎn)發(fā),有你們的 『點贊和評論』,才是我創(chuàng)造的動力。
- 關(guān)注小編,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識。
- 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點,請移步前往小編:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu