寫在開頭:
- 本文在 三方庫移植之NAPI開發(fā)[1]—Hello OpenHarmony NAPI 的基礎(chǔ)上修改hellonapi.cpp、index.ets辑莫,接著學(xué)習(xí)NAPI異步模型的Promise蜓氨、Callback方式筷频。
- 本文共有三個示例甚亭,分別是
Callback 異步接口
示例白对、Promise 異步接口
示例、規(guī)范異步接口
示例简烘。在本文末尾的資源中提供了這三個示例的源代碼,讀者可以下載在開發(fā)板上運(yùn)行定枷。 - 開發(fā)基于最新的OpenHarmony3.2Beta3版本及API9,標(biāo)準(zhǔn)系統(tǒng)開發(fā)板為潤和軟件DAYU200孤澎。
NAPI異步方式實(shí)現(xiàn)原理
同步方式和異步方式:
同步方式,所有的代碼處理都在原生方法(主線程)中完成欠窒。
異步方式覆旭,所有的代碼處理在多個線程中完成。實(shí)現(xiàn)NAPI異步方法的步驟:
1)立即返回一個臨時結(jié)果給js調(diào)用者
2)另起線程完成異步業(yè)務(wù)邏輯的執(zhí)行
3)通過callback或promise返回真正的結(jié)果異步工作項(xiàng)工作時序圖:
- 原生方法被調(diào)用時岖妄,原生方法完成
數(shù)據(jù)接收
型将、數(shù)據(jù)類型轉(zhuǎn)換
、存入上下文數(shù)據(jù)
荐虐,之后創(chuàng)建異步工作項(xiàng)
-
異步工作項(xiàng)
會加入調(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里有兩個回調(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)中處理梗肝。
- execute函數(shù)用于執(zhí)行工作項(xiàng)的業(yè)務(wù)邏輯碰缔,異步工作項(xiàng)被調(diào)度后账劲,該函數(shù)從上下文數(shù)據(jù)中獲取輸入數(shù)據(jù),在worker線程中完成
- 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í)行。
- 業(yè)務(wù)邏輯處理execute函數(shù)執(zhí)行完成或被取消后榛瓮,觸發(fā)EventLoop執(zhí)行complete函數(shù),complete函數(shù)從上下文數(shù)據(jù)中獲取結(jié)果巫击,轉(zhuǎn)換為JS類型禀晓,調(diào)用
- 管理簡單的異步操作的方法還有這些
- 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_delete_async_work(napi_env env, napi_async_work work)
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特性支持恨豁,則異步方法必須同時支持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í)現(xiàn)將異步執(zhí)行任務(wù)
- 任務(wù)執(zhí)行結(jié)果以參數(shù)的形式提供給用戶注冊的回調(diào)函數(shù),這些參數(shù)的第一個是 Error 或 undefined 類型跌捆,分別表示執(zhí)行出錯與正常。
- Promise異步模型
- 對象的狀態(tài)不受外界影響象颖;
- 一旦狀態(tài)改變了就不會再變佩厚,也就是說任何時候Promise都只有一種狀態(tài)。
- ES6原生提供了Promise對象说订,Promise是異步編程的一種解決方案抄瓦,可以替代傳統(tǒng)的解決方案回調(diào)函數(shù)和事件潮瓶;
- promise對象是一個異步操作的結(jié)果,提供了一些API使得異步執(zhí)行可以按照同步的流表示出來钙姊,避免了層層嵌套的回調(diào)函數(shù)毯辅,保證了回調(diào)是以異步的方式進(jìn)行調(diào)用的;
- 用戶在調(diào)用這些接口的時候煞额,接口實(shí)現(xiàn)將異步執(zhí)行任務(wù)思恐,同時返回一個 Promise 對象,其代表異步操作的結(jié)果膊毁;
- 在返回的結(jié)果的個數(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ì)算坪圾,不阻塞主線程。此處用一個加法簡單示意兽泄。
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個參數(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)建成功后通過最后一個參數(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è)備啟動時自動調(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)工作時序圖中位置芥喇,在圖中用紅框標(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ù)類型武通,則拋出異趁樱“Wrong 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)力九,則拋出異忱ㄉ祝“Callback 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)工作時序圖中位置,在圖中用紅框標(biāo)記如下
- 異步方法需要在不同線程中傳遞各種
業(yè)務(wù)數(shù)據(jù)
(上下文數(shù)據(jù)
),就需要定義一個結(jié)構(gòu)體保存這些被傳遞的信息孵奶。用于在主線程方法载绿、Work線程、EventLoop線程之間傳遞數(shù)據(jù)。
struct 結(jié)構(gòu)體名(也就是可選標(biāo)記名){ 成員變量;}灌诅;//使用分號即舌;表示定義結(jié)束盯仪。
- 本示例定義的上下文數(shù)據(jù)包含:
異步工作項(xiàng)對象
、回調(diào)函數(shù)
滞伟、2個參數(shù)(加數(shù)、被加數(shù)
)、業(yè)務(wù)邏輯處理結(jié)果
等4個屬性。
// 定義異步工作項(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個輸入?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ù)也是以對象的形式存在的篮赢,每個函數(shù)都是一個Function對象寥茫。
- 定義好結(jié)構(gòu)體后,接著我們將接收到的3個參數(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ù)定義如下:
- number類型的(
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: 需要創(chuàng)建一個引用的napi_value對象
[in] initial_refcount: 初始化引用次數(shù)崎页。
[out] result: 指針挑辆,指向新創(chuàng)建的napi_ref對象间唉。 返回值:返回napi_ok表示轉(zhuǎn)換成功率触,其他值失敗。
- napi_ref引用對象在原生方法退出后不自動回收,由用戶管理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)建一個napi_ref皱炉,這個napi_ref是可以跨作用域傳遞的悯搔,然后在需要用到的地方用napi_get_reference_value()
方法將napi_ref還原為napi_value靡馁,用完后再用napi_delete_reference()
方法刪除引用對象以便釋放相關(guān)內(nèi)存資源。
- 用戶管理napi_ref類型對象的生命周期的方法有
static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
// NAPI定義API方法時的接收參數(shù)為(napi_env, napi_callback_info)
// 其中napi_callback_info為上下文的信息。
size_t argc = 3; // 有3個參數(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個參數(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)工作時序圖中位置绩蜻,在圖中用紅框標(biāo)記如下
- 第一步:在創(chuàng)建
異步工作項(xiàng)
前铣墨,分別聲明addExecuteCB、addAsyncCompleteCB這2個函數(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個函數(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)建成功后通過最后一個參數(shù)接收async work的handle
napi_value resourceName = nullptr;
// 根據(jù)UTF8編碼格式的 C/C++字符串 創(chuàng)建一個 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)情況下直接傳入即可叁鉴。
//第二個參數(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)工作時序圖中位置,在圖中用紅框標(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)建的異步工作對象跟继,用于管理異步工作線程。
...
}
主線程:原生方法返回臨時返回值
調(diào)用napi_queue_async_work()將
異步工作項(xiàng)
加入調(diào)度隊(duì)列镣丑,由異步work線程池統(tǒng)一調(diào)度舔糖,原生方法返回空值退出。用戶在調(diào)用接口的時候莺匠,接口實(shí)現(xiàn)將異步執(zhí)行任務(wù),任務(wù)執(zhí)行結(jié)果以參數(shù)的形式提供給用戶注冊的回調(diào)函數(shù)(callback),這些參數(shù)的第一個是 Error 或 undefined 類型金吗,分別表示執(zhí)行出錯與正常。
static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
...
// 為異步方法創(chuàng)建臨時返回值,在此處原生方法臨時返回值是一個空對象
napi_value result = 0;
// callback接口返回參數(shù)為void趣竣,用napi_get_null()構(gòu)造一個空對象的返回值即可摇庙。
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)工作時序圖中位置卫袒,在圖中用紅框標(biāo)記如下
創(chuàng)建異步工作項(xiàng)前,聲明了addExecuteCB這個函數(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ì)算是一個簡單的加法迹冤,并把計(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ù)邏輯膜蠢,
// 不阻塞主線程堪藐。此處是一個加法
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)工作時序圖中位置礁竞,在圖中用紅框標(biāo)記如下
- 創(chuàng)建
異步工作項(xiàng)
前,聲明addAsyncCompleteCB這個函數(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)等對象综看。
- 第一步:addAsyncCompleteCB從接收到的
// 業(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)用返回一個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ù)個數(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引用對象在原生方法退出后不自動回收,由用戶管理napi_ref類型對象的生命周期扑馁。napi_delete_async_work()
用于刪除異步工作線程,在異步調(diào)用的結(jié)尾釋放async_work和相關(guān)業(yè)務(wù)數(shù)據(jù)的內(nèi)存
Callback異步接口總結(jié)
以下圖片為個人總結(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ì)算,不阻塞主線程乱投。此處用一個加法簡單示意咽笼。
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個參數(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)建成功后通過最后一個參數(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è)備啟動時自動調(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個對象——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ù)
- 定義一個上下文數(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個參數(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傳入的是三個參數(shù)表蝙,Promise方式在addPromise傳入的是兩個參數(shù)拴测。
創(chuàng)建異步工作項(xiàng)
同Callback方式一樣在創(chuàng)建異步工作項(xiàng)前,分別聲明2個函數(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)建成功后通過最后一個參數(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ì)算,不阻塞主線程澡腾。此處用一個加法簡單示意沸伏。
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ì)算,不阻塞主線程喇肋。此處用一個加法簡單示意坟乾。
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個參數(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)建成功后通過最后一個參數(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)建成功后通過最后一個參數(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è)備啟動時自動調(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線程直至獲取到返回值畔师。
命名:動詞+Sync或動詞+名詞+Sync
-
格式:
- 無參:方法名()
- 有參:方法名Sync(必填參數(shù)[, 可選參數(shù)])
返回值:有
聲明文件模板
declare namespace 模塊名
{
/**
* 方法描述
* @note 特殊說明
* @since (可選,方法支持版本與模塊不一致時需標(biāo)明)
* @sysCap 系統(tǒng)能力
* @devices 支持設(shè)備 (可選牧牢,支持設(shè)備類型與模塊不一致時需標(biāo)明)
* @param 參數(shù) 參數(shù)說明(可選看锉,沒有參數(shù)或參數(shù)用interface包含時不需要標(biāo)明)
* @return 返回值說明(可選,沒有返回值或返回值用interface包含時不需要標(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)用整個過程不會阻礙調(diào)用者的工作塔鳍。
命名:動詞或動詞+名詞
-
格式:
- 無參:方法名([回調(diào)函數(shù)])
- 有參:方法名(必填參數(shù)[, 可選參數(shù)][, 回調(diào)函數(shù)])
-
返回值
- 若回調(diào)函數(shù)非空伯铣,則返回void
- 若回調(diào)函數(shù)為空,則返回Promise實(shí)例對象
聲明文件模板
declare namespace 模塊名 {
/**
* 方法描述
* @note 特殊說明
* @since (可選轮纫,方法支持版本與模塊不一致時需標(biāo)明)
* @sysCap 系統(tǒng)能力
* @devices 支持設(shè)備 (可選腔寡,支持設(shè)備類型與模塊不一致時需標(biāo)明)
* @param 參數(shù) 參數(shù)說明(可選,沒有參數(shù)或參數(shù)用interface包含時不需要標(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 指令后炒辉,就會尋找指令后面<>中的文件名,并把這個文件的內(nèi)容包含到當(dāng)前文件中泉手。被包含文件中的文本將替換源代碼文件中的#include 指令
- 以typedef struct napi_env__* napi_env為例黔寇,搜遍Node.js的源碼都找不到napi_value__定義,那這個定義是什么意思呢斩萌?c語言中缝裤,允許定義一個沒有定義的結(jié)構(gòu)體的指針状囱。所以napi_value其實(shí)就是一個一級指針。他不需要類型信息倘是。
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)鏡像時瘤睹,會完整的編譯出
boot_linux.img
升敲、config.cfg
、MiniLoaderAll.bin
轰传、parameter.txt
驴党、ramdisk.img
、resource.img
获茬、system.img
港庄、uboot.img
、updater.img
恕曲、userdata.img
鹏氧、vendor.img
文件 - 后面自己修改源碼(不涉及內(nèi)核源碼)后編譯,可以燒錄自己編譯的
system.img
佩谣、vendor.img
把还、updater.img
、userdata.img
茸俭、ramdisk.img
寫在最后
- 如果你覺得這篇內(nèi)容對你還蠻有幫助吊履,我想邀請你幫我三個小忙:
- 點(diǎn)贊,轉(zhuǎn)發(fā)调鬓,有你們的 『點(diǎn)贊和評論』艇炎,才是我創(chuàng)造的動力。
- 關(guān)注小編袖迎,同時可以期待后續(xù)文章ing??冕臭,不定期分享原創(chuàng)知識腺晾。
- 想要獲取更多完整鴻蒙最新學(xué)習(xí)知識點(diǎn)燕锥,請移步前往小編:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu