北向應(yīng)用集成三方庫——NAPI 導(dǎo)出類對象

簡介

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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容