写在开头:
Callback 异步接口
示例、Promise 异步接口
示例、规范异步接口
示例。在本文末尾的资源中提供了这三个示例的源代码,读者可以下载在开发板上运行。数据接收
、数据类型转换
、存入上下文数据
,之后创建异步工作项
异步工作项
会加入调度队列,由异步工作线程池
统一调度,原生方法返回空值(Callback方式)或返回Promise对象(Promise方式)。napi_create_async_work()
函数创建异步工作项
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)
参数说明:
in env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
in async_resource: 可选项,关联async_hooks。
in async_resource_name: 异步资源标识符,主要用于async_hooks API暴露断言诊断信息。
in execute: 执行业务逻辑计算函数,由worker线程池调度执行。在该函数中执行IO、CPU密集型任务,不阻塞主线程。
in complete: execute参数指定的函数执行完成或取消后,触发执行该函数。此函数在EventLoop线程中执行。
in data: 用户提供的上下文数据,用于传递数据。
out result: napi_async_work*指针,用于返回当前此处函数调用创建的异步工作项。 返回值:返回napi_ok表示转换成功,其他值失败。
napi_create_async_work里有两个回调:
业务逻辑计算
(不阻塞主线程)并将结果写入上下文数据。JS回调函数
或通过Promise resolve()
返回结果。ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案回调函数和事件;promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的; 用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果; 在返回的结果的个数超过一个时,其以对象属性的形式返回。 ES6:全称ECMAScript 6.0。ECMAScript 是JavaScript语言的国际标准,JavaScript是ECMAScript的实现。
hellonapi.cpp文件
#include <string.h>
#include <stdio.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"
// 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
struct AddonData {
napi_async_work asyncWork = nullptr;
napi_deferred deferred = nullptr;
napi_ref callback = nullptr;
double args[2] = {0};
double result = 0;
};
// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
AddonData *addonData = (AddonData *)data;
// 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
addonData->result = addonData->args[0] + addonData->args[1];
}
// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
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;
// 执行回调函数
napi_call_function(env, undefined, callback, 1, &result, &callbackResult);
// 删除napi_ref对象
if (addonData->callback != nullptr) {
napi_delete_reference(env, addonData->callback);
}
// 删除异步工作项
napi_delete_async_work(env, addonData->asyncWork);
delete addonData;
}
static napi_value addCallback(napi_env env, napi_callback_info info) {
// 获取3个参数,值的类型是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参数类型
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;
}
// 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
auto addonData = new AddonData{
.asyncWork = nullptr,
};
// 将接收到的参数传入用户自定义上下文数据
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));
// 创建async work,创建成功后通过最后一个参数接收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);
// 将刚创建的async work加到队列,由底层去调度执行
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,指定当前NAPI模块对应的模块名
//以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: 模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
//示例对应eTS代码为:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = registerFunc, // 模块对外接口注册函数
.nm_modname = "hellonapi", // 自定义模块名
.nm_priv = ((void*)0),
.reserved = { 0 },
};
// 模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把模块定义的模块注册到系统中
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传入参数
在异步工作项工作时序图中位置,在图中用红框标记如下
// 获取并判断js参数类型
napi_valuetype valuetype0;
NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
// 使用napi_typeof接口进行参数类型的判断
napi_valuetype valuetype1;
NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
// 如果valuetype2调用的不是数据类型,则抛出异常“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调用的不是function类型(callback),则抛出异常“Callback function expected”
if (valuetype2 != napi_function) {
napi_throw_type_error(env, nullptr, "Callback function expected.");
return NULL;
}
初始化上下文数据
在异步工作项工作时序图中位置,在图中用红框标记如下
业务数据
(上下文数据
),就需要定义一个结构体保存这些被传递的信息。用于在主线程方法、Work线程、EventLoop线程之间传递数据。struct 结构体名(也就是可选标记名){ 成员变量;};//使用分号;表示定义结束。
异步工作项对象
、回调函数
、2个参数(加数、被加数
)、业务逻辑处理结果
等4个属性。// 定义异步工作项上下文数据
// 用户提供的上下文数据,用于在主线程方法、Work线程、EventLoop线程之间传递数据。
struct AddonData {
napi_async_work asyncWork = nullptr; //异步工作对象asyncWork
napi_ref callback = nullptr; //回调函数callback
double args[2] = {0}; //2个输入参数
double result = 0; //业务逻辑处理结果result(返回值)
};
数据类型
和Function类型
,都已统一封装为napi_value类型,故可如获取数据类型
的参数一样获取Function类型
的参数。Function是JavaScript提供的一种引用类型,通过Function类型创建Function对象。 在JavaScript中,函数也是以对象的形式存在的,每个函数都是一个Function对象。
加数
、被加数
、回调函数
)转换存入上下文数据完成初始化上下文数据
,加数
、被加数
)转换为double直接存入。回调函数
)怎么处理?不能直接存入napi_value类型。NAPI_EXTERN napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result);
参数说明:
in env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
in value: 需要创建一个引用的napi_value对象
in initial_refcount: 初始化引用次数。
out result: 指针,指向新创建的napi_ref对象。 返回值:返回napi_ok表示转换成功,其他值失败。
napi_create_reference()
方法将napi_value创建一个napi_ref,这个napi_ref是可以跨作用域传递的,然后在需要用到的地方用napi_get_reference_value()
方法将napi_ref还原为napi_value,用完后再用napi_delete_reference()
方法删除引用对象以便释放相关内存资源。static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
// NAPI定义API方法时的接收参数为(napi_env, napi_callback_info)
// 其中napi_callback_info为上下文的信息。
size_t argc = 3; // 有3个参数(`加数`、`被加数`、`回调函数`)到上下文中
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中获取参数列表、this及其他数据。
...
// 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
// 创建结构体addonData用于保存各种需要在异步线程中传递的数据信息
auto addonData = new AddonData{
.asyncWork = nullptr,
};
// 将接收到的3个参数(`加数`、`被加数`、`回调函数`)传入用户自定义上下文数据
NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
// NAPI_CALL()是用来调用NAPI中的API的
// NAPI提供napi_get_value_double方法将JS类型double值转换为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));
//调用napi_create_reference()函数将接收到的napi_value类型的回调函数callback转换为napi_ref类型,将napi_value包装成napi_ref引用对象。并保存到asyncContext上下文数据中,以便后续在C++异步线程中能够回调该js fuction类型。
//参数解释如下
// env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可
// args[2]: 引用的napi_value对象 (加数和被加数)
// 1:初始化引用1次
// &addonData->callback: 指向新创建的napi_ref 对象(callback)
...
}
创建异步工作项
在异步工作项工作时序图中位置,在图中用红框标记如下
异步工作项
前,分别声明addExecuteCB、addAsyncCompleteCB这2个函数,分别用作于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)函数的execute、complete参数。异步工作项
,将addExecuteCB、addAsyncCompleteCB这2个函数存入上下文数据的asyncWork属性// 业务逻辑处理函数,由异步work线程池统一调度
static void addExecuteCB(napi_env env, void *data) {
}
// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发。
static void addAsyncCompleteCB(napi_env env, napi_status status, void *data) {
}
static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
...
// 创建async work,创建成功后通过最后一个参数接收async work的handle
napi_value resourceName = nullptr;
// 根据UTF8编码格式的 C/C++字符串 创建一个 JS字符串对象.
// 传入的参数是Javascript值类型,被NAPI框架封装成统一的唯一类型——napi_value类型,为了能够进行计算,我们需要获取其对应在C/C++中的类型的值。将C/C++ utf8类型的值转为node_value类型,返回给JS代码
napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName); //参数说明如下
//env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可
//addCallback:定义的上下文信息中的addCallback对象
//NAPI_AUTO_LENGTH:字符长度
//resourceName:创建的napi_value对象
// 异步方式依赖NAPI框架提供的napi_create_async_work()函数创建异步工作项
napi_create_async_work(env, nullptr, resourceName, addExecuteCB , addCallbackCompleteCB , (void *)addonData,&addonData->asyncWork);
//参数说明如下
//env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
//第二个参数是nullptr
//resourceName: 定义的上下文信息中的addCallback对象(异步资源标识符),主要用于async_hooks API暴露断言诊断信息。
//addExecuteCB:执行业务逻辑计算函数,由worker线程池调度执行。在该函数中执行IO、CPU密集型任务,不阻塞主线程。
//addCallbackCompleteCB: execute参数指定的函数执行完成或取消后,触发执行该函数。此函数在EventLoop线程中执行。
//(void *)addonData: 用户提供的上下文数据,用于传递数据。
//&addonData->asyncWork: 用于返回当前此处函数调用创建的异步工作项。 返回值:返回napi_ok表示转换成功,其他值失败。
...
}
异步工作项加入队列,等待调度
在异步工作项工作时序图中位置,在图中用红框标记如下
static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
...
// 将刚创建的异步工作项(async work)加到队列,由work thread调度执行
napi_queue_async_work(env, addonData->asyncWork);
// 其中asyncWork是上下文数据中创建的异步工作对象,用于管理异步工作线程。
...
}
异步工作项
加入调度队列,由异步work线程池统一调度,原生方法返回空值退出。static napi_value addAsyncCallback(napi_env env, napi_callback_info info) {
...
// 为异步方法创建临时返回值,在此处原生方法临时返回值是一个空对象
napi_value result = 0;
// callback接口返回参数为void,用napi_get_null()构造一个空对象的返回值即可。
NAPI_CALL(env, napi_get_null(env, &result));
return result;
}
执行业务逻辑
、把计算结果写入上下文数据
在异步工作项工作时序图中位置,在图中用红框标记如下
创建异步工作项前,声明了addExecuteCB这个函数,用作于napi_create_async_work()函数的execute参数。
执行业务逻辑
:业务逻辑计算是一个简单的加法,并把计算结果存入上下文数据
的result属性把计算结果写入上下文数据
:把execute函数的结构体指向上下文数据
中结构体。// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
// 把计算结果写入上下文数据,把addonData指向AddonData
AddonData *addonData = (AddonData *)data;
// 执行业务逻辑,
// 不阻塞主线程。此处是一个加法
addonData->result = addonData->args[0] + addonData->args[1];
}
把上下文中的结果转为JS类型
、调用JS回调函数
在异步工作项工作时序图中位置,在图中用红框标记如下
异步工作项
前,声明addAsyncCompleteCB这个函数,用作于napi_create_async_work()函数的complete参数。上下文数据
中获取结果,调用napi_call_function()方法执行JS回调函数返回数据给JS。// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
//所有的接口调用返回一个napi_status类型的状态码,用来表明接口调用成功或者失败
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;
// 执行回调函数
napi_call_function(env, undefined, callback, 1, &result, &callbackResult);
// 删除napi_ref对象
if (addonData->callback != nullptr) {
napi_delete_reference(env, addonData->callback);
}
// 删除异步工作项
napi_delete_async_work(env, addonData->asyncWork);
delete addonData;
}
napi_call_function()
函数供扩展Natvie代码(C/C++代码)调用JS函数,用于执行回调函数等场景。函数定义如下:// 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)
参数说明:
in env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
in recv: 传给被调用的this对象。
in func: 被调用的函数.
in argc: 函数参数个数(对应函数数组的长度)。
in argv: 函数参数数组.
out result: func函数执行的返回值。 返回值:返回napi_ok表示转换成功,其他值失败。
上下文数据
的callback属性的类型为napi_ref,需要调用napi_get_reference_value()
函数获取其指向的napi_value对象值才调用napi_call_function()
函数。 napi_get_reference_value函数定义:// 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)
参数说明:
in env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
in ref: napi_ref对象
out result: napi_ref引用的napi_value对象。 返回值:返回napi_ok表示转换成功,其他值失败。
napi_delete_reference()
用于删除上下文数据
中定义的napi_ref对象callback。napi_ref引用对象在原生方法退出后不自动回收,由用户管理napi_ref类型对象的生命周期。napi_delete_async_work()
用于删除异步工作线程,在异步调用的结尾释放async_work和相关业务数据的内存以下图片为个人总结,可以在文末下载清晰的图片,下载之后推荐到diagrams。
hellonapi.cpp
#include <string.h>
#include<stdio.h>
#include "napi/native_node_api.h"
#include "napi/native_api.h"
// 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
struct AddonData {
napi_async_work asyncWork = nullptr;
napi_deferred deferred = nullptr;
napi_ref callback = nullptr;
double args[2] = {0};
double result = 0;
};
// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
AddonData *addonData = (AddonData *)data;
// 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
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);
}
// 删除异步工作项
napi_delete_async_work(env, addonData->asyncWork);
delete addonData;
addonData = nullptr;
}
static napi_value addPromise(napi_env env, napi_callback_info info) {
// 获取2个参数,值的类型是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参数类型
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;
}
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
// 异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据
auto addonData = new AddonData{
.asyncWork = nullptr,
.deferred = deferred,
};
// 将接收到的参数传入
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]));
// 创建async work,创建成功后通过最后一个参数(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);
// 将刚创建的async work加到队列,由底层去调度执行
napi_queue_async_work(env, addonData->asyncWork);
// 原生方法返回promise
return promise;
}
// napi_addon_register_func
//2.指定模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
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,指定当前NAPI模块对应的模块名
//以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: 模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
//示例对应eTS代码为:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = registerFunc, // 模块对外接口注册函数
.nm_modname = "hellonapi", // 自定义模块名
.nm_priv = ((void*)0),
.reserved = { 0 },
};
//3.模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把模块定义的模块注册到系统中
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;
函数定义如下:
napi_status napi_create_promise(napi_env env,
napi_deferred* deferred,
napi_value* promise);
参数说明:
in env: 传入接口调用者的环境,包含js引擎等,由框架提供,默认情况下直接传入即可。
out deferred: 返回接收刚创建的deferred对象,关联Promise对象,后面使用napi_resolve_deferred() 或 napi_reject_deferred() 返回数据。
out promise: 关联上面deferred对象的JS Promise对象 返回值:返回napi_ok表示转换成功,其他值失败。
static napi_value addPromise(napi_env env, napi_callback_info info) {
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
// 创建promise对象。promise用于返回promise对象给js调用者
NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
...
// 返回promise
return promise;
}
// 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
struct AddonData {
...
napi_deferred deferred = nullptr;
double args[2] = {0};
...
};
static napi_value addPromise(napi_env env, napi_callback_info info) {
// 获取2个参数,值的类型是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));
...
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
...
// 将接收到的参数传入
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]));
...
}
// 用户提供的上下文数据,在原生方法(初始化数据)、executeCB、completeCB之间传递数据
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) {
...
// 创建async work,创建成功后通过最后一个参数(addonData->asyncWork)用于后续在C++的异步线程中返回真正的计算结果
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);
// 将刚创建的async work加到队列,由底层去调度执行
napi_queue_async_work(env, addonData->asyncWork);
// 原生方法返回promise
return promise;
}
此处完全同Callback方式,无需修改。
// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
AddonData *addonData = (AddonData *)data;
// 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
addonData->result = addonData->args[0] + addonData->args[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);
}
// 删除异步工作项
napi_delete_async_work(env, addonData->asyncWork);
delete addonData;
addonData = nullptr;
}
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;
};
// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
AddonData *addonData = (AddonData *)data;
// 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
addonData->result = addonData->args[0] + addonData->args[1];
}
// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由EventLoop线程中执行。
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;
// 执行回调函数
napi_call_function(env, undefined, callback, 1, &result, &callbackResult);
// 删除napi_ref对象
if (addonData->callback != nullptr) {
napi_delete_reference(env, addonData->callback);
}
// 删除异步工作项
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);
}
// 删除异步工作项
napi_delete_async_work(env, addonData->asyncWork);
delete addonData;
}
static napi_value addAsync(napi_env env, napi_callback_info info) {
// 获取3个参数,值的类型是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参数类型
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;
}
// 异步工作项上下文用户数据,传递到异步工作项的execute、complete中传递数据
auto addonData = new AddonData{
.asyncWork = nullptr,
};
if (argc == 2) {
// 创建promise
napi_value promise = nullptr;
napi_deferred deferred = nullptr;
NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
addonData->deferred = deferred;
// 将接收到的参数传入
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]));
// 创建async work,创建成功后通过最后一个参数(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);
// 将刚创建的async work加到队列,由底层去调度执行
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;
}
// 将接收到的参数传入用户自定义上下文数据
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));
// 创建async work,创建成功后通过最后一个参数接收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);
// 将刚创建的async work加到队列,由底层去调度执行
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,指定当前NAPI模块对应的模块名
//以及模块注册对外接口的处理函数,具体扩展的接口在该函数中声明
// nm_modname: 模块名称,对应eTS代码为import nm_modname from '@ohos.ohos_shared_library_name'
//示例对应eTS代码为:import hellonapi from '@ohos.hellonapi'
static napi_module hellonapiModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = registerFunc, // 模块对外接口注册函数
.nm_modname = "hellonapi", // 自定义模块名
.nm_priv = ((void*)0),
.reserved = { 0 },
};
//3.模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)函数注册到系统中。
// register module,设备启动时自动调用此constructor函数,把模块定义的模块注册到系统中
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;
declare namespace 模块名
{
/**
* 方法描述
* @note 特殊说明
* @since (可选,方法支持版本与模块不一致时需标明)
* @sysCap 系统能力
* @devices 支持设备 (可选,支持设备类型与模块不一致时需标明)
* @param 参数 参数说明(可选,没有参数或参数用interface包含时不需要标明)
* @return 返回值说明(可选,没有返回值或返回值用interface包含时不需要标明)
*/
// 无参
function 方法名Sync(): 返回值类型;
// 有参
function 方法名Sync(必填参数: 参数类型, options?: 可选参数类型): 返回值类型;
interface 可选参数类型 {
参数名: 参数类型;
}
}
export default 模块名;
declare namespace hellonapi {
function add(num1: number, num2: number): number;
/**
*
*
* @since 9
* @syscap SystemCapability.Ability.AbilityRuntime.AbilityCore
*/
}
export default hellonapi;
declare namespace 模块名 {
/**
* 方法描述
* @note 特殊说明
* @since (可选,方法支持版本与模块不一致时需标明)
* @sysCap 系统能力
* @devices 支持设备 (可选,支持设备类型与模块不一致时需标明)
* @param 参数 参数说明(可选,没有参数或参数用interface包含时不需要标明)
*/
// 无参
function 方法名(callback: AsyncCallback<结果数据类型>): void;
function 方法名(): Promise<结果数据类型>;
// 有参
function 方法名(必填参数: 参数类型, callback: AsyncCallback<结果数据类型>): void;
function 方法名(必填参数: 参数类型, options: 可选参数类型, callback: AsyncCallback<结果数据类型>): void;
function 方法名(必填参数: 参数类型, options?: 可选参数类型): Promise<结果数据类型>;
interface 可选参数类型 {
参数名: 参数类型;
}
}
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;
通过查看foundation/arkui/napi/interfaces/inner_api/napi/native_node_api.h(编写NAPI拓展模块hellonapi.cpp需要包含的头文件)可以知道OpenHarmony基本的NAPI数据类型。
#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内容的差别。
两者内容一致,可以推测OpenHarmony中基本的NAPI数据类型和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;
预处理器发现 #include 指令后,就会寻找指令后面<>中的文件名,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源代码文件中的#include 指令
typedef作用就是定义类型别名
符号类型 | 符号名 | 备注 |
---|---|---|
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 |
符号类型 | 符号名 | 备注 |
---|---|---|
FUNC | napi_run_script_path | 运行JavaScript文件 |
boot_linux.img
、config.cfg
、MiniLoaderAll.bin
、parameter.txt
、ramdisk.img
、resource.img
、system.img
、uboot.img
、updater.img
、userdata.img
、vendor.img
文件system.img
、vendor.img
、updater.img
、userdata.img
、ramdisk.img
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。