三方庫移植之NAPI開發(fā)[4]異步調(diào)用:Callback&Promise

寫在開頭

  • 本文在 三方庫移植之NAPI開發(fā)[1]—Hello OpenHarmony NAPI 的基礎(chǔ)上修改hellonapi.cpp、index.ets,接著學(xué)習(xí)NAPI異步模型的Promise、Callback方式。
  • 本文共有三個(gè)示例,分別是Callback 異步接口示例、Promise 異步接口示例、規(guī)范異步接口示例。在本文末尾的資源中提供了這三個(gè)示例的源代碼,讀者可以下載在開發(fā)板上運(yùn)行。
  • 開發(fā)基于最新的OpenHarmony3.2Beta3版本及API9,標(biāo)準(zhǔn)系統(tǒng)開發(fā)板為潤和軟件DAYU200。

NAPI異步方式實(shí)現(xiàn)原理

  • 同步方式和異步方式
    同步方式,所有的代碼處理都在原生方法(主線程)中完成。
    異步方式,所有的代碼處理在多個(gè)線程中完成。

  • 實(shí)現(xiàn)NAPI異步方法的步驟
    1)立即返回一個(gè)臨時(shí)結(jié)果給js調(diào)用者
    2)另起線程完成異步業(yè)務(wù)邏輯的執(zhí)行
    3)通過callback或promise返回真正的結(jié)果

  • 異步工作項(xiàng)工作時(shí)序圖

  • 原生方法被調(diào)用時(shí),原生方法完成數(shù)據(jù)接收數(shù)據(jù)類型轉(zhuǎn)換、存入上下文數(shù)據(jù),之后創(chuàng)建異步工作項(xiàng)
  • 異步工作項(xiàng)會(huì)加入調(diào)度隊(duì)列,由異步工作線程池統(tǒng)一調(diào)度,原生方法返回空值(Callback方式)或返回Promise對象(Promise方式)。
  • 異步方式依賴NAPI框架提供的napi_create_async_work()函數(shù)創(chuàng)建異步工作項(xiàng)
    napi_create_async_work()在foundation/arkui/napi/native_engine/native_node_api.cpp第71行
NAPI_EXTERN napi_status napi_create_async_work(napi_env env,
                                               napi_value async_resource,
                                               napi_value async_resource_name,
                                               napi_async_execute_callback execute,
                                               napi_async_complete_callback complete,
                                               void* data,
                                               napi_async_work* result)

參數(shù)說明
[in] env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可。
[in] async_resource: 可選項(xiàng),關(guān)聯(lián)async_hooks。
[in] async_resource_name: 異步資源標(biāo)識符,主要用于async_hooks API暴露斷言診斷信息。
[in] execute: 執(zhí)行業(yè)務(wù)邏輯計(jì)算函數(shù),由worker線程池調(diào)度執(zhí)行。在該函數(shù)中執(zhí)行IO、CPU密集型任務(wù),不阻塞主線程。
[in] complete: execute參數(shù)指定的函數(shù)執(zhí)行完成或取消后,觸發(fā)執(zhí)行該函數(shù)。此函數(shù)在EventLoop線程中執(zhí)行。
[in] data: 用戶提供的上下文數(shù)據(jù),用于傳遞數(shù)據(jù)。
[out] result: napi_async_work*指針,用于返回當(dāng)前此處函數(shù)調(diào)用創(chuàng)建的異步工作項(xiàng)。 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。

napi_create_async_work里有兩個(gè)回調(diào):

  • execute
    • execute函數(shù)用于執(zhí)行工作項(xiàng)的業(yè)務(wù)邏輯,異步工作項(xiàng)被調(diào)度后,該函數(shù)從上下文數(shù)據(jù)中獲取輸入數(shù)據(jù),在worker線程中完成業(yè)務(wù)邏輯計(jì)算(不阻塞主線程)并將結(jié)果寫入上下文數(shù)據(jù)。
    • 因?yàn)閑xecute函數(shù)不在JS線程中,所以不允許execute函數(shù)調(diào)用napi的接口。業(yè)務(wù)邏輯的返回值可以返回到complete回調(diào)中處理。
  • complete
    • 業(yè)務(wù)邏輯處理execute函數(shù)執(zhí)行完成或被取消后,觸發(fā)EventLoop執(zhí)行complete函數(shù),complete函數(shù)從上下文數(shù)據(jù)中獲取結(jié)果,轉(zhuǎn)換為JS類型,調(diào)用JS回調(diào)函數(shù)通過Promise resolve()返回結(jié)果。
    • 可以調(diào)用napi的接口,將execute中的返回值封裝成JS對象返回。此回調(diào)在JS線程中執(zhí)行。
  • 管理簡單的異步操作的方法還有這些
    • napi_delete_async_work(napi_env env, napi_async_work work)
      刪除異步工作線程
    • napi_queue_async_work(napi_env env, napi_async_work work)
      將剛創(chuàng)建的異步工作項(xiàng)加到隊(duì)列(排隊(duì)),由底層去調(diào)度執(zhí)行
    • napi_cancel_async_work(napi_env env, napi_async_work work)
      取消異步工作項(xiàng)

NAPI支持異步模型

  • OpenHarmony標(biāo)準(zhǔn)系統(tǒng)異步接口實(shí)現(xiàn)支持Promise方式和Callback方式。NAPI支持異步模型,提供了Promise、Callback方式。
  • 標(biāo)準(zhǔn)系統(tǒng)異步接口實(shí)現(xiàn)規(guī)范要求,若引擎開啟Promise特性支持,則異步方法必須同時(shí)支持Callback方式和Promise方式。
    • 由應(yīng)用開發(fā)者決定使用哪種方式,通過是否傳遞Callback函數(shù)區(qū)分異步方法是Callback方式還是Promise方式
    • 不傳遞Callback即為Promise方式(方法執(zhí)行結(jié)果為Promise實(shí)例對象),否則為Callback方式
  • Promise、Callback 異步模型都是 OHOS 標(biāo)準(zhǔn)異步模型。
  • Callback異步模型
    • 用戶在調(diào)用接口的時(shí)候,接口實(shí)現(xiàn)將異步執(zhí)行任務(wù)
    • 任務(wù)執(zhí)行結(jié)果以參數(shù)的形式提供給用戶注冊的回調(diào)函數(shù),這些參數(shù)的第一個(gè)是 Error 或 undefined 類型,分別表示執(zhí)行出錯(cuò)與正常。
  • Promise異步模型
    • 對象的狀態(tài)不受外界影響;
    • 一旦狀態(tài)改變了就不會(huì)再變,也就是說任何時(shí)候Promise都只有一種狀態(tài)。
  • ES6原生提供了Promise對象,Promise是異步編程的一種解決方案,可以替代傳統(tǒng)的解決方案回調(diào)函數(shù)和事件;
    • promise對象是一個(gè)異步操作的結(jié)果,提供了一些API使得異步執(zhí)行可以按照同步的流表示出來,避免了層層嵌套的回調(diào)函數(shù),保證了回調(diào)是以異步的方式進(jìn)行調(diào)用的;
    • 用戶在調(diào)用這些接口的時(shí)候,接口實(shí)現(xiàn)將異步執(zhí)行任務(wù),同時(shí)返回一個(gè) Promise 對象,其代表異步操作的結(jié)果;
    • 在返回的結(jié)果的個(gè)數(shù)超過一個(gè)時(shí),其以對象屬性的形式返回。
  • ES6:全稱ECMAScript 6.0。ECMAScript 是JavaScript語言的國際標(biāo)準(zhǔn),JavaScript是ECMAScript的實(shí)現(xiàn)。

Callback 異步接口

Callback 異步接口示例代碼

hellonapi.cpp文件

#include <string.h>
#include <stdio.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

// 用戶提供的上下文數(shù)據(jù),在原生方法(初始化數(shù)據(jù))、executeCB、completeCB之間傳遞數(shù)據(jù)
struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;
  napi_ref callback = nullptr;
  double args[2] = {0};
  double result = 0;
};

// 業(yè)務(wù)邏輯處理函數(shù),由worker線程池調(diào)度執(zhí)行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = (AddonData *)data;

  // 執(zhí)行復(fù)雜計(jì)算,不阻塞主線程。此處用一個(gè)加法簡單示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

// 業(yè)務(wù)邏輯處理完成回調(diào)函數(shù),在業(yè)務(wù)邏輯處理函數(shù)執(zhí)行完成或取消后觸發(fā),由EventLoop線程中執(zhí)行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value callback = nullptr;
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_value callbackResult = nullptr;

  // 執(zhí)行回調(diào)函數(shù)
  napi_call_function(env, undefined, callback, 1, &result, &callbackResult);

  // 刪除napi_ref對象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 刪除異步工作項(xiàng)
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static napi_value addCallback(napi_env env, napi_callback_info info) {
  // 獲取3個(gè)參數(shù),值的類型是js類型(napi_value)
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 獲取并判斷js參數(shù)類型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  napi_valuetype valuetype2;
  NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
  if (valuetype2 != napi_function) {
    napi_throw_type_error(env, nullptr, "Callback function expected.");
    return NULL;
  }

  // 異步工作項(xiàng)上下文用戶數(shù)據(jù),傳遞到異步工作項(xiàng)的execute、complete中傳遞數(shù)據(jù)
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  // 將接收到的參數(shù)傳入用戶自定義上下文數(shù)據(jù)
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
  NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

  // 創(chuàng)建async work,創(chuàng)建成功后通過最后一個(gè)參數(shù)接收async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addCallbackCompleteCB, (void *)addonData,
                         &addonData->asyncWork);

  // 將剛創(chuàng)建的async work加到隊(duì)列,由底層去調(diào)度執(zhí)行
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回空對象
  napi_value result = 0;
  NAPI_CALL(env, napi_get_null(env, &result));
  return result;
}

// napi_addon_register_func
static napi_value registerFunc(napi_env env, napi_value exports) {
  static napi_property_descriptor desc[] = {
      DECLARE_NAPI_FUNCTION("addCallback", addCallback),
  };
  NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
  return exports;
}

// 定義napi_module,指定當(dāng)前NAPI模塊對應(yīng)的模塊名
//以及模塊注冊對外接口的處理函數(shù),具體擴(kuò)展的接口在該函數(shù)中聲明
// nm_modname: 模塊名稱,對應(yīng)eTS代碼為import nm_modname from '@ohos.ohos_shared_library_name'
//示例對應(yīng)eTS代碼為:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc, // 模塊對外接口注冊函數(shù)
    .nm_modname = "hellonapi",  // 自定義模塊名
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

// 模塊定義好后,調(diào)用NAPI提供的模塊注冊函數(shù)napi_module_register(napi_module* mod)函數(shù)注冊到系統(tǒng)中。
// register module,設(shè)備啟動(dòng)時(shí)自動(dòng)調(diào)用此constructor函數(shù),把模塊定義的模塊注冊到系統(tǒng)中
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    napi_module_register(&hellonapiModule);
}

index.ets

import prompt from '@system.prompt';
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
struct TestAdd {
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Button("hellonapi.addCallback(x, y, callback)").margin(10).fontSize(20).onClick(() => {
        let num1 = 123, num2 = 456
        hellonapi.addCallback(num1, num2, (result) => {
          prompt.showToast({ message: `hellonapi.addCallback(${num1}, ${num2}) = ${result}` })
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

@ohos.hellonapi.d.ts

declare namespace hellonapi {

    function addCallback(num1: number, num2: number, callback:(result: number) => void): void;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;

主線程:獲取JS傳入?yún)?shù)

獲取JS傳入?yún)?shù)在異步工作項(xiàng)工作時(shí)序圖中位置,在圖中用紅框標(biāo)記如下

  // 獲取并判斷js參數(shù)類型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
// 使用napi_typeof接口進(jìn)行參數(shù)類型的判斷
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

// 如果valuetype2調(diào)用的不是數(shù)據(jù)類型,則拋出異?!癢rong arguments. 2 numbers expected.”
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  napi_valuetype valuetype2;
  NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));

// 如果valuetype2調(diào)用的不是function類型(callback),則拋出異?!癈allback function expected”
  if (valuetype2 != napi_function) {
    napi_throw_type_error(env, nullptr, "Callback function expected.");
    return NULL;
  }
  • 使用napi_typeof接口進(jìn)行參數(shù)類型的判斷
  • NAPI_CALL()是用來調(diào)用NAPI中的API的。

主線程:初始化上下文數(shù)據(jù)

初始化上下文數(shù)據(jù)在異步工作項(xiàng)工作時(shí)序圖中位置,在圖中用紅框標(biāo)記如下

  • 異步方法需要在不同線程中傳遞各種業(yè)務(wù)數(shù)據(jù)上下文數(shù)據(jù)),就需要定義一個(gè)結(jié)構(gòu)體保存這些被傳遞的信息。用于在主線程方法、Work線程、EventLoop線程之間傳遞數(shù)據(jù)。

struct 結(jié)構(gòu)體名(也就是可選標(biāo)記名){ 成員變量;};//使用分號;表示定義結(jié)束。

  • 本示例定義的上下文數(shù)據(jù)包含:異步工作項(xiàng)對象、回調(diào)函數(shù)、2個(gè)參數(shù)(加數(shù)、被加數(shù))、業(yè)務(wù)邏輯處理結(jié)果等4個(gè)屬性。
// 定義異步工作項(xiàng)上下文數(shù)據(jù)
// 用戶提供的上下文數(shù)據(jù),用于在主線程方法、Work線程、EventLoop線程之間傳遞數(shù)據(jù)。
struct AddonData {
  napi_async_work asyncWork = nullptr;   //異步工作對象asyncWork
  napi_ref callback = nullptr;           //回調(diào)函數(shù)callback
  double args[2] = {0};                  //2個(gè)輸入?yún)?shù)
  double result = 0;                     //業(yè)務(wù)邏輯處理結(jié)果result(返回值)
};
  • OpenHarmony的NAPI框架將ECMAScript標(biāo)準(zhǔn)中定義的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八種數(shù)據(jù)類型Function類型,都已統(tǒng)一封裝為napi_value類型,故可如獲取數(shù)據(jù)類型的參數(shù)一樣獲取Function類型的參數(shù)。

Function是JavaScript提供的一種引用類型,通過Function類型創(chuàng)建Function對象。
在JavaScript中,函數(shù)也是以對象的形式存在的,每個(gè)函數(shù)都是一個(gè)Function對象。

  • 定義好結(jié)構(gòu)體后,接著我們將接收到的3個(gè)參數(shù)(加數(shù)、被加數(shù)、回調(diào)函數(shù))轉(zhuǎn)換存入上下文數(shù)據(jù)完成初始化上下文數(shù)據(jù),
    • number類型的(加數(shù)、被加數(shù))轉(zhuǎn)換為double直接存入。
    • Function類型的參數(shù)(回調(diào)函數(shù))怎么處理?不能直接存入napi_value類型。
      • 因?yàn)闋可娴絅API對象生命周期管理問題。napi_value類型引用對象的生命周期在原生方法退出后結(jié)束,后面在work線程無法獲取其值。
      • NAPI提供了一種生命期限長于原生方法的對象引用類型—— napi_ref,所以調(diào)用napi_create_reference()函數(shù)將接收到的napi_value類型的回調(diào)函數(shù)參數(shù)callback轉(zhuǎn)換為napi_ref類型。napi_create_reference()函數(shù)定義如下:
NAPI_EXTERN napi_status napi_create_reference(napi_env env,
                                              napi_value value,
                                              uint32_t initial_refcount,
                                              napi_ref* result);

參數(shù)說明:
[in] env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可。
[in] value: 需要?jiǎng)?chuàng)建一個(gè)引用的napi_value對象
[in] initial_refcount: 初始化引用次數(shù)。
[out] result: 指針,指向新創(chuàng)建的napi_ref對象。 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。

  • napi_ref引用對象在原生方法退出后不自動(dòng)回收,由用戶管理napi_ref類型對象的生命周期。
    • 用戶管理napi_ref類型對象的生命周期的方法有
      • napi_create_reference() : 將napi_value包裝成napi_ref引用對象
      • napi_get_reference_value() : 從napi_ref引用對象中取得napi_value
      • napi_delete_reference() :刪除napi_ref引用對象
    • 通過napi_create_reference()方法將napi_value創(chuàng)建一個(gè)napi_ref,這個(gè)napi_ref是可以跨作用域傳遞的,然后在需要用到的地方用napi_get_reference_value()方法將napi_ref還原為napi_value,用完后再用napi_delete_reference()方法刪除引用對象以便釋放相關(guān)內(nèi)存資源。
static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
// NAPI定義API方法時(shí)的接收參數(shù)為(napi_env, napi_callback_info)
// 其中napi_callback_info為上下文的信息。

  size_t argc = 3;   // 有3個(gè)參數(shù)(`加數(shù)`、`被加數(shù)`、`回調(diào)函數(shù)`)到上下文中
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
// NAPI提供了napi_get_cb_info()方法可從napi_callback_info中獲取參數(shù)列表、this及其他數(shù)據(jù)。

  ...
  // 異步工作項(xiàng)上下文用戶數(shù)據(jù),傳遞到異步工作項(xiàng)的execute、complete中傳遞數(shù)據(jù)
  // 創(chuàng)建結(jié)構(gòu)體addonData用于保存各種需要在異步線程中傳遞的數(shù)據(jù)信息
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  // 將接收到的3個(gè)參數(shù)(`加數(shù)`、`被加數(shù)`、`回調(diào)函數(shù)`)傳入用戶自定義上下文數(shù)據(jù)
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));

  // NAPI_CALL()是用來調(diào)用NAPI中的API的
  // NAPI提供napi_get_value_double方法將JS類型double值轉(zhuǎn)換為C++類型的double值
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));  

  NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));
  //調(diào)用napi_create_reference()函數(shù)將接收到的napi_value類型的回調(diào)函數(shù)callback轉(zhuǎn)換為napi_ref類型,將napi_value包裝成napi_ref引用對象。并保存到asyncContext上下文數(shù)據(jù)中,以便后續(xù)在C++異步線程中能夠回調(diào)該js fuction類型。
  //參數(shù)解釋如下
     // env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可
     // args[2]: 引用的napi_value對象 (加數(shù)和被加數(shù))
     // 1:初始化引用1次
     // &addonData->callback: 指向新創(chuàng)建的napi_ref 對象(callback) 
  ...
}
  • NAPI_CALL()是用來調(diào)用NAPI中的API的。

主線程:創(chuàng)建異步工作項(xiàng)

創(chuàng)建異步工作項(xiàng)在異步工作項(xiàng)工作時(shí)序圖中位置,在圖中用紅框標(biāo)記如下

  • 第一步:在創(chuàng)建異步工作項(xiàng)前,分別聲明addExecuteCB、addAsyncCompleteCB這2個(gè)函數(shù),分別用作于napi_create_async_work(napi_env env,napi_value async_resource,napi_value async_resource_name,napi_async_execute_callback execute,napi_async_complete_callback complete,void* data,napi_async_work* result)函數(shù)的execute、complete參數(shù)。
  • 第二步:利用NAPI框架提供的napi_create_async_work()函數(shù)創(chuàng)建異步工作項(xiàng),將addExecuteCB、addAsyncCompleteCB這2個(gè)函數(shù)存入上下文數(shù)據(jù)的asyncWork屬性
// 業(yè)務(wù)邏輯處理函數(shù),由異步work線程池統(tǒng)一調(diào)度
static void addExecuteCB(napi_env env, void *data) {
}

// 業(yè)務(wù)邏輯處理完成回調(diào)函數(shù),在業(yè)務(wù)邏輯處理函數(shù)執(zhí)行完成或取消后觸發(fā)。
static void addAsyncCompleteCB(napi_env env, napi_status status, void *data) {
}

static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
  ...
    // 創(chuàng)建async work,創(chuàng)建成功后通過最后一個(gè)參數(shù)接收async work的handle
    napi_value resourceName = nullptr;

// 根據(jù)UTF8編碼格式的 C/C++字符串 創(chuàng)建一個(gè) JS字符串對象.
// 傳入的參數(shù)是Javascript值類型,被NAPI框架封裝成統(tǒng)一的唯一類型——napi_value類型,為了能夠進(jìn)行計(jì)算,我們需要獲取其對應(yīng)在C/C++中的類型的值。將C/C++ utf8類型的值轉(zhuǎn)為node_value類型,返回給JS代碼
    napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName); //參數(shù)說明如下
    //env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可
    //addCallback:定義的上下文信息中的addCallback對象
    //NAPI_AUTO_LENGTH:字符長度
    //resourceName:創(chuàng)建的napi_value對象

   // 異步方式依賴NAPI框架提供的napi_create_async_work()函數(shù)創(chuàng)建異步工作項(xiàng)
   napi_create_async_work(env, nullptr, resourceName,   addExecuteCB  ,  addCallbackCompleteCB  ,   (void *)addonData,&addonData->asyncWork);
   //參數(shù)說明如下
   //env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可。
   //第二個(gè)參數(shù)是nullptr
   //resourceName: 定義的上下文信息中的addCallback對象(異步資源標(biāo)識符),主要用于async_hooks API暴露斷言診斷信息。
   //addExecuteCB:執(zhí)行業(yè)務(wù)邏輯計(jì)算函數(shù),由worker線程池調(diào)度執(zhí)行。在該函數(shù)中執(zhí)行IO、CPU密集型任務(wù),不阻塞主線程。
   //addCallbackCompleteCB: execute參數(shù)指定的函數(shù)執(zhí)行完成或取消后,觸發(fā)執(zhí)行該函數(shù)。此函數(shù)在EventLoop線程中執(zhí)行。
   //(void *)addonData: 用戶提供的上下文數(shù)據(jù),用于傳遞數(shù)據(jù)。
   //&addonData->asyncWork: 用于返回當(dāng)前此處函數(shù)調(diào)用創(chuàng)建的異步工作項(xiàng)。 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。
  ...
}

主線程:異步工作項(xiàng)加入隊(duì)列,等待調(diào)度

異步工作項(xiàng)加入隊(duì)列,等待調(diào)度在異步工作項(xiàng)工作時(shí)序圖中位置,在圖中用紅框標(biāo)記如下

static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
  ...
  // 將剛創(chuàng)建的異步工作項(xiàng)(async work)加到隊(duì)列,由work thread調(diào)度執(zhí)行
  napi_queue_async_work(env, addonData->asyncWork);
  // 其中asyncWork是上下文數(shù)據(jù)中創(chuàng)建的異步工作對象,用于管理異步工作線程。
  ...
}

主線程:原生方法返回臨時(shí)返回值

  • 調(diào)用napi_queue_async_work()將異步工作項(xiàng)加入調(diào)度隊(duì)列,由異步work線程池統(tǒng)一調(diào)度,原生方法返回空值退出。

  • 用戶在調(diào)用接口的時(shí)候,接口實(shí)現(xiàn)將異步執(zhí)行任務(wù),任務(wù)執(zhí)行結(jié)果以參數(shù)的形式提供給用戶注冊的回調(diào)函數(shù)(callback),這些參數(shù)的第一個(gè)是 Error 或 undefined 類型,分別表示執(zhí)行出錯(cuò)與正常。

static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
  ...
  // 為異步方法創(chuàng)建臨時(shí)返回值,在此處原生方法臨時(shí)返回值是一個(gè)空對象
  napi_value result = 0;
  // callback接口返回參數(shù)為void,用napi_get_null()構(gòu)造一個(gè)空對象的返回值即可。
  NAPI_CALL(env, napi_get_null(env, &result));
  return result;
}

work線程:執(zhí)行業(yè)務(wù)邏輯、把計(jì)算結(jié)果寫入上下文數(shù)據(jù)

執(zhí)行業(yè)務(wù)邏輯、把計(jì)算結(jié)果寫入上下文數(shù)據(jù)在異步工作項(xiàng)工作時(shí)序圖中位置,在圖中用紅框標(biāo)記如下

創(chuàng)建異步工作項(xiàng)前,聲明了addExecuteCB這個(gè)函數(shù),用作于napi_create_async_work()函數(shù)的execute參數(shù)。

  • execute函數(shù)在異步工作項(xiàng)被調(diào)度后在work線程中執(zhí)行

    • 不阻塞主線程(不阻塞UI界面)
    • 可執(zhí)行IO、CPU密集型等任務(wù)。
  • 執(zhí)行業(yè)務(wù)邏輯:業(yè)務(wù)邏輯計(jì)算是一個(gè)簡單的加法,并把計(jì)算結(jié)果存入上下文數(shù)據(jù)的result屬性

  • 把計(jì)算結(jié)果寫入上下文數(shù)據(jù):把execute函數(shù)的結(jié)構(gòu)體指向上下文數(shù)據(jù)中結(jié)構(gòu)體。

// 業(yè)務(wù)邏輯處理函數(shù),由worker線程池調(diào)度執(zhí)行。
static void addExecuteCB(napi_env env, void *data) {

 // 把計(jì)算結(jié)果寫入上下文數(shù)據(jù),把a(bǔ)ddonData指向AddonData
  AddonData *addonData = (AddonData *)data;

  // 執(zhí)行業(yè)務(wù)邏輯,
  // 不阻塞主線程。此處是一個(gè)加法
  addonData->result = addonData->args[0] + addonData->args[1];
}

EventLoop線程:把上下文中的結(jié)果轉(zhuǎn)為JS類型、調(diào)用JS回調(diào)函數(shù)

把上下文中的結(jié)果轉(zhuǎn)為JS類型、調(diào)用JS回調(diào)函數(shù)在異步工作項(xiàng)工作時(shí)序圖中位置,在圖中用紅框標(biāo)記如下

  • 創(chuàng)建異步工作項(xiàng)前,聲明addAsyncCompleteCB這個(gè)函數(shù),用作于napi_create_async_work()函數(shù)的complete參數(shù)。
    • 第一步:addAsyncCompleteCB從接收到的上下文數(shù)據(jù)中獲取結(jié)果,調(diào)用napi_call_function()方法執(zhí)行JS回調(diào)函數(shù)返回?cái)?shù)據(jù)給JS。
    • 第二步: 釋放(刪除)過程中創(chuàng)建的napi_ref引用對象、異步工作項(xiàng)等對象。
// 業(yè)務(wù)邏輯處理完成回調(diào)函數(shù),在業(yè)務(wù)邏輯處理函數(shù)執(zhí)行完成或取消后觸發(fā),由EventLoop線程中執(zhí)行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
//所有的接口調(diào)用返回一個(gè)napi_status類型的狀態(tài)碼,用來表明接口調(diào)用成功或者失敗

  AddonData *addonData = (AddonData *)data;

  napi_value callback = nullptr;
  //從napi_ref引用對象中取得napi_value
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_value callbackResult = nullptr;
  // 執(zhí)行回調(diào)函數(shù)
  napi_call_function(env, undefined, callback, 1, &result, &callbackResult);

  // 刪除napi_ref對象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 刪除異步工作項(xiàng)
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}
  • NAPI框架提供了napi_call_function()函數(shù)供擴(kuò)展Natvie代碼(C/C++代碼)調(diào)用JS函數(shù),用于執(zhí)行回調(diào)函數(shù)等場景。函數(shù)定義如下:
// Methods to work with Functions
NAPI_EXTERN napi_status napi_call_function(napi_env env,
                                           napi_value recv,
                                           napi_value func,
                                           size_t argc,
                                           const napi_value* argv,
                                           napi_value* result)

參數(shù)說明:
[in] env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可。
[in] recv: 傳給被調(diào)用的this對象。
[in] func: 被調(diào)用的函數(shù).
[in] argc: 函數(shù)參數(shù)個(gè)數(shù)(對應(yīng)函數(shù)數(shù)組的長度)。
[in] argv: 函數(shù)參數(shù)數(shù)組.
[out] result: func函數(shù)執(zhí)行的返回值。 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。

  • 因?qū)ο笊芷诠芾韱栴},上下文數(shù)據(jù)的callback屬性的類型為napi_ref,需要調(diào)用napi_get_reference_value()函數(shù)獲取其指向的napi_value對象值才調(diào)用napi_call_function()函數(shù)。 napi_get_reference_value函數(shù)定義:
// Attempts to get a referenced value. If the reference is weak,
// the value might no longer be available, in that case the call
// is still successful but the result is nullptr.
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, 
                                                 napi_ref ref,  
                                                 napi_value* result)

參數(shù)說明:
[in] env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可。
[in] ref: napi_ref對象
[out] result: napi_ref引用的napi_value對象。 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。

  • 執(zhí)行回調(diào)函數(shù)是為了在異步操作之后調(diào)用JS函數(shù)

  • napi_delete_reference()用于刪除上下文數(shù)據(jù)中定義的napi_ref對象callback。napi_ref引用對象在原生方法退出后不自動(dòng)回收,由用戶管理napi_ref類型對象的生命周期。

  • napi_delete_async_work()用于刪除異步工作線程,在異步調(diào)用的結(jié)尾釋放async_work和相關(guān)業(yè)務(wù)數(shù)據(jù)的內(nèi)存

Callback異步接口總結(jié)

以下圖片為個(gè)人總結(jié),可以在文末下載清晰的圖片,下載之后推薦到[diagrams]。

Promise異步接口

Promise異步接口示例代碼

hellonapi.cpp

#include <string.h>
#include<stdio.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

// 用戶提供的上下文數(shù)據(jù),在原生方法(初始化數(shù)據(jù))、executeCB、completeCB之間傳遞數(shù)據(jù)
struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;

  napi_ref callback = nullptr;

  double args[2] = {0};

  double result = 0;

};

// 業(yè)務(wù)邏輯處理函數(shù),由worker線程池調(diào)度執(zhí)行。
static void addExecuteCB(napi_env env, void *data) {

  AddonData *addonData = (AddonData *)data;

  // 執(zhí)行復(fù)雜計(jì)算,不阻塞主線程。此處用一個(gè)加法簡單示意。
  addonData->result = addonData->args[0] + addonData->args[1];

  // addonData->result = addonData->args[0] + addonData[1];
}

static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_resolve_deferred(env, addonData->deferred, result);

     // 刪除napi_ref對象
     if (addonData->callback != nullptr) {
     napi_delete_reference(env, addonData->callback);
     }

  // 刪除異步工作項(xiàng)
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
  addonData = nullptr;
}

static napi_value addPromise(napi_env env, napi_callback_info info) {
  // 獲取2個(gè)參數(shù),值的類型是js類型(napi_value)
  size_t argc = 2;
  napi_value args[2];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 獲取并判斷js參數(shù)類型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  // 創(chuàng)建promise
  napi_value promise = nullptr;
  napi_deferred deferred = nullptr;
  NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

  // 異步工作項(xiàng)上下文用戶數(shù)據(jù),傳遞到異步工作項(xiàng)的execute、complete之間傳遞數(shù)據(jù)
  auto addonData = new AddonData{
      .asyncWork = nullptr,
      .deferred = deferred,
  };

  // 將接收到的參數(shù)傳入
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));

  // 創(chuàng)建async work,創(chuàng)建成功后通過最后一個(gè)參數(shù)(addonData->asyncWork)返回async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                         &addonData->asyncWork);

  // 將剛創(chuàng)建的async work加到隊(duì)列,由底層去調(diào)度執(zhí)行
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回promise
  return promise;

}

// napi_addon_register_func
//2.指定模塊注冊對外接口的處理函數(shù),具體擴(kuò)展的接口在該函數(shù)中聲明
static napi_value registerFunc(napi_env env, napi_value exports)
{
    static napi_property_descriptor desc[] = {
        { "addPromise", nullptr, addPromise, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;   
}

// 1.先定義napi_module,指定當(dāng)前NAPI模塊對應(yīng)的模塊名
//以及模塊注冊對外接口的處理函數(shù),具體擴(kuò)展的接口在該函數(shù)中聲明
// nm_modname: 模塊名稱,對應(yīng)eTS代碼為import nm_modname from '@ohos.ohos_shared_library_name'
//示例對應(yīng)eTS代碼為:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc, // 模塊對外接口注冊函數(shù)
    .nm_modname = "hellonapi",  // 自定義模塊名
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

//3.模塊定義好后,調(diào)用NAPI提供的模塊注冊函數(shù)napi_module_register(napi_module* mod)函數(shù)注冊到系統(tǒng)中。
// register module,設(shè)備啟動(dòng)時(shí)自動(dòng)調(diào)用此constructor函數(shù),把模塊定義的模塊注冊到系統(tǒng)中
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    napi_module_register(&hellonapiModule);
}

index.ets

import prompt from '@system.prompt';
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
struct TestAdd {
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Button("hellonapi.addPromise(x, y).then(...)").margin(1).fontSize(20).onClick(() => {
        let num1 = 123, num2 = 456
        hellonapi.addPromise(num1, num2).then((result) => {
          prompt.showToast({ message: `hellonapi.addPromise(${num1}, ${num2}) = ${result}` })
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

@ohos.hellonapi.d.ts

declare namespace hellonapi {

    function addPromise(num1: number, num2: number): Promise<number>;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;

創(chuàng)建Promise

  • Promise整體處理流程和Callback方式一樣,在此小節(jié)只討論P(yáng)romise不同于Callback的部分

  • 首先創(chuàng)建Promise,NAPI框架中提供了napi_create_promise()函數(shù)用于創(chuàng)建Promise,調(diào)用該函數(shù)輸出2個(gè)對象——deferred、promise。

    • promise用于原生方法返回,deferred傳入異步工作項(xiàng)的上下文數(shù)據(jù)。complete函數(shù)中,應(yīng)用napi_resolve_deferred()函數(shù) 或 napi_reject_deferred() 函數(shù)返回?cái)?shù)據(jù)。

函數(shù)定義如下:

napi_status napi_create_promise(napi_env env,
                                napi_deferred* deferred,
                                napi_value* promise);

參數(shù)說明:

[in] env: 傳入接口調(diào)用者的環(huán)境,包含js引擎等,由框架提供,默認(rèn)情況下直接傳入即可。
[out] deferred: 返回接收剛創(chuàng)建的deferred對象,關(guān)聯(lián)Promise對象,后面使用napi_resolve_deferred() 或 napi_reject_deferred() 返回?cái)?shù)據(jù)。
[out] promise: 關(guān)聯(lián)上面deferred對象的JS Promise對象 返回值:返回napi_ok表示轉(zhuǎn)換成功,其他值失敗。

static napi_value addPromise(napi_env env, napi_callback_info info) {
  // 創(chuàng)建promise
  napi_value promise = nullptr;
  napi_deferred deferred = nullptr;

// 創(chuàng)建promise對象。promise用于返回promise對象給js調(diào)用者
  NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

  ...

  // 返回promise
  return promise;
}

初始化上下文數(shù)據(jù)

  • 定義一個(gè)上下文數(shù)據(jù)結(jié)構(gòu),用于保存和傳遞數(shù)據(jù)。Promise方式加上deferred屬性。
// 用戶提供的上下文數(shù)據(jù),在原生方法(初始化數(shù)據(jù))、executeCB、completeCB之間傳遞數(shù)據(jù)
struct AddonData {
  ...
  napi_deferred deferred = nullptr;
  double args[2] = {0};
  ...
};

static napi_value addPromise(napi_env env, napi_callback_info info) {
  // 獲取2個(gè)參數(shù),值的類型是js類型(napi_value)
  size_t argc = 2;
  napi_value args[2];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));
  ...
  // 創(chuàng)建promise
  napi_value promise = nullptr;
  napi_deferred deferred = nullptr;
  NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
  ...
  // 將接收到的參數(shù)傳入
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
  ...
}
  • Callback方式在addCallback傳入的是三個(gè)參數(shù),Promise方式在addPromise傳入的是兩個(gè)參數(shù)。

創(chuàng)建異步工作項(xiàng)

  • 同Callback方式一樣在創(chuàng)建異步工作項(xiàng)前,分別聲明2個(gè)函數(shù),分別用作于napi_create_async_work()函數(shù)的execute、complete參數(shù)。

  • 異步工作項(xiàng)創(chuàng)建OK后,將其存入上下文數(shù)據(jù)的asyncWork屬性,并調(diào)用napi_queue_async_work()將異步工作項(xiàng)加入調(diào)度隊(duì)列,由異步work線程池統(tǒng)一調(diào)度,原生方法返回Promise對象退出。

// 用戶提供的上下文數(shù)據(jù),在原生方法(初始化數(shù)據(jù))、executeCB、completeCB之間傳遞數(shù)據(jù)
struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;
  double args[2] = {0};
  double result = 0;
};

static napi_value addPromise(napi_env env, napi_callback_info info) {
  ...
  // 創(chuàng)建async work,創(chuàng)建成功后通過最后一個(gè)參數(shù)(addonData->asyncWork)用于后續(xù)在C++的異步線程中返回真正的計(jì)算結(jié)果
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                         &addonData->asyncWork);

  // 將剛創(chuàng)建的async work加到隊(duì)列,由底層去調(diào)度執(zhí)行
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回promise
  return promise;
}

execute 回調(diào)處理

此處完全同Callback方式,無需修改。

// 業(yè)務(wù)邏輯處理函數(shù),由worker線程池調(diào)度執(zhí)行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = (AddonData *)data;

  // 執(zhí)行復(fù)雜計(jì)算,不阻塞主線程。此處用一個(gè)加法簡單示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

complete 回調(diào)處理

  • 調(diào)用NAPI提供的napi_resolve_deferred() 或 napi_reject_deferred() 返回?cái)?shù)據(jù)。之后釋放過程中創(chuàng)建的napi_ref引用對象、異步工作項(xiàng)等對象。
static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_resolve_deferred(env, addonData->deferred, result);

     // 刪除napi_ref對象
     if (addonData->callback != nullptr) {
     napi_delete_reference(env, addonData->callback);
     }

  // 刪除異步工作項(xiàng)
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
  addonData = nullptr;
}

規(guī)范異步接口

hellonapi.cpp

#include <string.h>
#include<stdio.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"

struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;
  napi_ref callback = nullptr;
  double args[2] = {0};
  double result = 0;
};

// 業(yè)務(wù)邏輯處理函數(shù),由worker線程池調(diào)度執(zhí)行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = (AddonData *)data;

  // 執(zhí)行復(fù)雜計(jì)算,不阻塞主線程。此處用一個(gè)加法簡單示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

// 業(yè)務(wù)邏輯處理完成回調(diào)函數(shù),在業(yè)務(wù)邏輯處理函數(shù)執(zhí)行完成或取消后觸發(fā),由EventLoop線程中執(zhí)行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value callback = nullptr;
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_value callbackResult = nullptr;

  // 執(zhí)行回調(diào)函數(shù)
  napi_call_function(env, undefined, callback, 1, &result, &callbackResult);

  // 刪除napi_ref對象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 刪除異步工作項(xiàng)
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = (AddonData *)data;
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_resolve_deferred(env, addonData->deferred, result);

  // 刪除napi_ref對象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 刪除異步工作項(xiàng)
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static napi_value addAsync(napi_env env, napi_callback_info info) {
  // 獲取3個(gè)參數(shù),值的類型是js類型(napi_value)
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 獲取并判斷js參數(shù)類型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  // 異步工作項(xiàng)上下文用戶數(shù)據(jù),傳遞到異步工作項(xiàng)的execute、complete中傳遞數(shù)據(jù)
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  if (argc == 2) {
    // 創(chuàng)建promise
    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
    addonData->deferred = deferred;

    // 將接收到的參數(shù)傳入
    NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
    NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));

    // 創(chuàng)建async work,創(chuàng)建成功后通過最后一個(gè)參數(shù)(addonData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "addPromise", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                           &addonData->asyncWork);

    // 將剛創(chuàng)建的async work加到隊(duì)列,由底層去調(diào)度執(zhí)行
    napi_queue_async_work(env, addonData->asyncWork);

    // 返回promise
    return promise;
  } else {
    napi_valuetype valuetype2;
    NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
    if (valuetype2 != napi_function) {
      napi_throw_type_error(env, nullptr, "Callback function expected.");
      return NULL;
    }

    // 將接收到的參數(shù)傳入用戶自定義上下文數(shù)據(jù)
    NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
    NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
    NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

    // 創(chuàng)建async work,創(chuàng)建成功后通過最后一個(gè)參數(shù)接收async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addCallbackCompleteCB, (void *)addonData,
                           &addonData->asyncWork);

    // 將剛創(chuàng)建的async work加到隊(duì)列,由底層去調(diào)度執(zhí)行
    napi_queue_async_work(env, addonData->asyncWork);

    // 原生方法返回空對象
    napi_value result = 0;
    NAPI_CALL(env, napi_get_null(env, &result));
    return result;
  }
}

// napi_addon_register_func
static napi_value registerFunc(napi_env env, napi_value exports) {
  static napi_property_descriptor desc[] = {

      DECLARE_NAPI_FUNCTION("addAsync", addAsync),
  };
  NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
  return exports;
}

// 1.先定義napi_module,指定當(dāng)前NAPI模塊對應(yīng)的模塊名
//以及模塊注冊對外接口的處理函數(shù),具體擴(kuò)展的接口在該函數(shù)中聲明
// nm_modname: 模塊名稱,對應(yīng)eTS代碼為import nm_modname from '@ohos.ohos_shared_library_name'
//示例對應(yīng)eTS代碼為:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerFunc, // 模塊對外接口注冊函數(shù)
    .nm_modname = "hellonapi",  // 自定義模塊名
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

//3.模塊定義好后,調(diào)用NAPI提供的模塊注冊函數(shù)napi_module_register(napi_module* mod)函數(shù)注冊到系統(tǒng)中。
// register module,設(shè)備啟動(dòng)時(shí)自動(dòng)調(diào)用此constructor函數(shù),把模塊定義的模塊注冊到系統(tǒng)中
extern "C" __attribute__((constructor)) void hellonapiModuleRegister()
{
    napi_module_register(&hellonapiModule);
}

index.ets

import prompt from '@system.prompt';
import hellonapi from '@ohos.hellonapi'

@Entry
@Component
struct TestAdd {
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {

      Button("hellonapi.addAsync(x, y, callback)").margin(10).fontSize(20).onClick(() => {
        let num1 = 123, num2 = 456
        hellonapi.addAsync(num1, num2, (result) => {
          prompt.showToast({ message: `hellonapi.addAsync(${num1}, ${num2}) = ${result}` })
        })
      })

      Button("hellonapi.addAsync(x, y).then(...)").margin(10).fontSize(20).onClick(() => {
        let num1 = 123, num2 = 456
        hellonapi.addAsync(num1, num2).then((result) => {
          prompt.showToast({ message: `hellonapi.addAsync(${num1}, ${num2}) = ${result}` })
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

@ohos.hellonapi.d.ts

declare namespace hellonapi {

    function addAsync(num1: number, num2: number, callback:(result: number) => void): void;
    function addAsync(num1: number, num2: number): Promise<number>;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;

異步方法和同步方法.ts接口文件

同步方法

  • 同步方法調(diào)用之后,將阻塞住JS線程直至獲取到返回值。

  • 命名:動(dòng)詞+Sync或動(dòng)詞+名詞+Sync

  • 格式:

    • 無參:方法名()
    • 有參:方法名Sync(必填參數(shù)[, 可選參數(shù)])
  • 返回值:有

  • 聲明文件模板

declare namespace 模塊名 
{

/**
* 方法描述
* @note 特殊說明
* @since (可選,方法支持版本與模塊不一致時(shí)需標(biāo)明)
* @sysCap 系統(tǒng)能力
* @devices 支持設(shè)備 (可選,支持設(shè)備類型與模塊不一致時(shí)需標(biāo)明)
* @param 參數(shù) 參數(shù)說明(可選,沒有參數(shù)或參數(shù)用interface包含時(shí)不需要標(biāo)明)
* @return 返回值說明(可選,沒有返回值或返回值用interface包含時(shí)不需要標(biāo)明)
*/

// 無參
function 方法名Sync(): 返回值類型;

// 有參
function 方法名Sync(必填參數(shù): 參數(shù)類型, options?: 可選參數(shù)類型): 返回值類型;

interface 可選參數(shù)類型 {
參數(shù)名: 參數(shù)類型;
}
}

export default 模塊名;
  • 示例
declare namespace hellonapi {
    function add(num1: number, num2: number): number;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;

異步方法

  • 異步方法調(diào)用整個(gè)過程不會(huì)阻礙調(diào)用者的工作。

  • 命名:動(dòng)詞或動(dòng)詞+名詞

  • 格式:

    • 無參:方法名([回調(diào)函數(shù)])
    • 有參:方法名(必填參數(shù)[, 可選參數(shù)][, 回調(diào)函數(shù)])
  • 返回值

    • 若回調(diào)函數(shù)非空,則返回void
    • 若回調(diào)函數(shù)為空,則返回Promise實(shí)例對象
  • 聲明文件模板

declare namespace 模塊名 {

/**
 * 方法描述
 * @note 特殊說明
 * @since (可選,方法支持版本與模塊不一致時(shí)需標(biāo)明)
 * @sysCap 系統(tǒng)能力
 * @devices 支持設(shè)備 (可選,支持設(shè)備類型與模塊不一致時(shí)需標(biāo)明)
 * @param 參數(shù) 參數(shù)說明(可選,沒有參數(shù)或參數(shù)用interface包含時(shí)不需要標(biāo)明)
 */

// 無參
function 方法名(callback: AsyncCallback<結(jié)果數(shù)據(jù)類型>): void;
function 方法名(): Promise<結(jié)果數(shù)據(jù)類型>;

// 有參
function 方法名(必填參數(shù): 參數(shù)類型, callback: AsyncCallback<結(jié)果數(shù)據(jù)類型>): void;
function 方法名(必填參數(shù): 參數(shù)類型, options: 可選參數(shù)類型, callback: AsyncCallback<結(jié)果數(shù)據(jù)類型>): void;
function 方法名(必填參數(shù): 參數(shù)類型, options?: 可選參數(shù)類型): Promise<結(jié)果數(shù)據(jù)類型>;

interface 可選參數(shù)類型 {
  參數(shù)名: 參數(shù)類型;
}
}

export default 模塊名;
  • 示例
declare namespace hellonapi {

    function addAsync(num1: number, num2: number, callback:(result: number) => void): void;
    function addAsync(num1: number, num2: number): Promise<number>;
    /**
     * 
     *
     * @since 9
     * @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
     */

}
export default hellonapi;   

NAPI中的數(shù)據(jù)類型

  • NAPI使用的數(shù)據(jù)類型和Node.js N-API保持一致。OpenHarmony的NAPI(Native API)組件是一套對外接口基于Node.js N-API規(guī)范開發(fā)的原生模塊擴(kuò)展開發(fā)框架。
    通過查看foundation/arkui/napi/interfaces/inner_api/napi/native_node_api.h(編寫NAPI拓展模塊hellonapi.cpp需要包含的頭文件)可以知道OpenHarmony基本的NAPI數(shù)據(jù)類型。

include <js_native_api.h>中的js_native_api.h在ohos3.2beta3版本源碼目錄下路徑為prebuilts/build-tools/common/nodejs/node-v12.18.4-linux-x64/include/node/js_native_api_types.h。

然后再分析prebuilts/build-tools/common/nodejs/node-v12.18.4-linux-x64/include/node/js_native_api_types.h和third_party/node/src/js_native_api_types.h內(nèi)容的差別。

兩者內(nèi)容一致,可以推測OpenHarmony中基本的NAPI數(shù)據(jù)類型和Node.js N-API中的保持一致。而接口名方面,napi提供的接口名與三方Node.js一致,目前支持部分接口,詳情見libnapi.ndk.json文件

// JSVM API types are all opaque pointers for ABI stability
// typedef undefined structs instead of void* for compile time type safety
typedef struct napi_env__* napi_env;
typedef struct napi_value__* napi_value;
typedef struct napi_ref__* napi_ref;
typedef struct napi_handle_scope__* napi_handle_scope;
typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope;
typedef struct napi_callback_info__* napi_callback_info;
typedef struct napi_deferred__* napi_deferred;

預(yù)處理器發(fā)現(xiàn) #include 指令后,就會(huì)尋找指令后面<>中的文件名,并把這個(gè)文件的內(nèi)容包含到當(dāng)前文件中。被包含文件中的文本將替換源代碼文件中的#include 指令

  • 以typedef struct napi_env__* napi_env為例,搜遍Node.js的源碼都找不到napi_value__定義,那這個(gè)定義是什么意思呢?c語言中,允許定義一個(gè)沒有定義的結(jié)構(gòu)體的指針。所以napi_value其實(shí)就是一個(gè)一級指針。他不需要類型信息。

typedef作用就是定義類型別名

關(guān)于NAPI標(biāo)準(zhǔn)庫中導(dǎo)出的符號列表

  • NAPI它基于Node.js N-API規(guī)范開發(fā),因此可參考Node.js N-API了解NAPI標(biāo)準(zhǔn)庫中符號列表。本文以3.2beta3源碼中的node三方庫為例,從third_party/node/README.OpenSource中可得知3.2beta3移植的node版本為14.19.1,因此可參考的Node.js N-API鏈接為14.19.1版本,如下:https://nodejs.org/docs/latest-v14.x/api/n-api.html

  • 標(biāo)準(zhǔn)庫中導(dǎo)出的符號列表

符號類型 符號名 備注
FUNC napi_module_register
FUNC napi_get_last_error_info
FUNC napi_throw
FUNC napi_throw_error
FUNC napi_throw_type_error
FUNC napi_throw_range_error
FUNC napi_is_error
FUNC napi_create_error
FUNC napi_create_type_error
FUNC napi_create_range_error
FUNC napi_get_and_clear_last_exception
FUNC napi_is_exception_pending
FUNC napi_fatal_error
FUNC napi_open_handle_scope
FUNC napi_close_handle_scope
FUNC napi_open_escapable_handle_scope
FUNC napi_close_escapable_handle_scope
FUNC napi_escape_handle
FUNC napi_create_reference
FUNC napi_delete_reference
FUNC napi_reference_ref
FUNC napi_reference_unref
FUNC napi_get_reference_value
FUNC napi_create_array
FUNC napi_create_array_with_length
FUNC napi_create_arraybuffer
FUNC napi_create_external
FUNC napi_create_external_arraybuffer
FUNC napi_create_object
FUNC napi_create_symbol
FUNC napi_create_typedarray
FUNC napi_create_dataview
FUNC napi_create_int32
FUNC napi_create_uint32
FUNC napi_create_int64
FUNC napi_create_double
FUNC napi_create_string_latin1
FUNC napi_create_string_utf8
FUNC napi_get_array_length
FUNC napi_get_arraybuffer_info
FUNC napi_get_prototype
FUNC napi_get_typedarray_info
FUNC napi_get_dataview_info
FUNC napi_get_value_bool
FUNC napi_get_value_double
FUNC napi_get_value_external
FUNC napi_get_value_int32
FUNC napi_get_value_int64
FUNC napi_get_value_string_latin1
FUNC napi_get_value_string_utf8
FUNC napi_get_value_uint32
FUNC napi_get_boolean
FUNC napi_get_global
FUNC napi_get_null
FUNC napi_get_undefined
FUNC napi_coerce_to_bool
FUNC napi_coerce_to_number
FUNC napi_coerce_to_object
FUNC napi_coerce_to_string
FUNC napi_typeof
FUNC napi_instanceof
FUNC napi_is_array
FUNC napi_is_arraybuffer
FUNC napi_is_typedarray
FUNC napi_is_dataview
FUNC napi_is_date
FUNC napi_strict_equals
FUNC napi_get_property_names
FUNC napi_set_property
FUNC napi_get_property
FUNC napi_has_property
FUNC napi_delete_property
FUNC napi_has_own_property
FUNC napi_set_named_property
FUNC napi_get_named_property
FUNC napi_has_named_property
FUNC napi_set_element
FUNC napi_get_element
FUNC napi_has_element
FUNC napi_delete_element
FUNC napi_define_properties
FUNC napi_call_function
FUNC napi_create_function
FUNC napi_get_cb_info
FUNC napi_get_new_target
FUNC napi_new_instance
FUNC napi_define_class
FUNC napi_wrap
FUNC napi_unwrap
FUNC napi_remove_wrap
FUNC napi_create_async_work
FUNC napi_delete_async_work
FUNC napi_queue_async_work
FUNC napi_cancel_async_work
FUNC napi_get_node_version
FUNC napi_get_version
FUNC napi_create_promise
FUNC napi_resolve_deferred
FUNC napi_reject_deferred
FUNC napi_is_promise
FUNC napi_run_script
FUNC napi_get_uv_event_loop

Native API接口說明

符號類型 符號名 備注
FUNC napi_run_script_path 運(yùn)行JavaScript文件

關(guān)于鏡像文件的編譯

  • 初次編譯OpenHarmony標(biāo)準(zhǔn)系統(tǒng)鏡像時(shí),會(huì)完整的編譯出boot_linux.img、config.cfg、MiniLoaderAll.bin、parameter.txt、ramdisk.img、resource.img、system.img、uboot.img、updater.imguserdata.img、vendor.img文件
  • 后面自己修改源碼(不涉及內(nèi)核源碼)后編譯,可以燒錄自己編譯的system.img、vendor.img、updater.imguserdata.img、ramdisk.img

寫在最后

  • 如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個(gè)小忙:
  • 點(diǎn)贊,轉(zhuǎn)發(fā),有你們的 『點(diǎn)贊和評論』,才是我創(chuàng)造的動(dòng)力。
  • 關(guān)注小編,同時(shí)可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識。
  • 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點(diǎn),請移步前往小編:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容