三方库移植之NAPI开发[4]异步调用:CallbackPromise

写在开头

  • 本文在 三方库移植之NAPI开发[1]—Hello OpenHarmony NAPI 的基础上修改hellonapi.cpp、index.ets,接着学习NAPI异步模型的Promise、Callback方式。
  • 本文共有三个示例,分别是Callback 异步接口示例、Promise 异步接口示例、规范异步接口示例。在本文末尾的资源中提供了这三个示例的源代码,读者可以下载在开发板上运行。
  • 开发基于最新的OpenHarmony3.2Beta3版本及API9,标准系统开发板为润和软件DAYU200。

NAPI异步方式实现原理

  • 同步方式和异步方式
    同步方式,所有的代码处理都在原生方法(主线程)中完成。
    异步方式,所有的代码处理在多个线程中完成。

  • 实现NAPI异步方法的步骤
    1)立即返回一个临时结果给js调用者
    2)另起线程完成异步业务逻辑的执行
    3)通过callback或promise返回真正的结果

  • 异步工作项工作时序图

  • 原生方法被调用时,原生方法完成数据接收数据类型转换存入上下文数据,之后创建异步工作项
  • 异步工作项会加入调度队列,由异步工作线程池统一调度,原生方法返回空值(Callback方式)或返回Promise对象(Promise方式)。
  • 异步方式依赖NAPI框架提供的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里有两个回调:

  • execute
    • execute函数用于执行工作项的业务逻辑,异步工作项被调度后,该函数从上下文数据中获取输入数据,在worker线程中完成业务逻辑计算(不阻塞主线程)并将结果写入上下文数据。
    • 因为execute函数不在JS线程中,所以不允许execute函数调用napi的接口。业务逻辑的返回值可以返回到complete回调中处理。
  • complete
    • 业务逻辑处理execute函数执行完成或被取消后,触发EventLoop执行complete函数,complete函数从上下文数据中获取结果,转换为JS类型,调用JS回调函数通过Promise resolve()返回结果。
    • 可以调用napi的接口,将execute中的返回值封装成JS对象返回。此回调在JS线程中执行。
  • 管理简单的异步操作的方法还有这些
    • napi_delete_async_work(napi_env env, napi_async_work work)
      删除异步工作线程
    • napi_queue_async_work(napi_env env, napi_async_work work)
      将刚创建的异步工作项加到队列(排队),由底层去调度执行
    • napi_cancel_async_work(napi_env env, napi_async_work work)
      取消异步工作项

NAPI支持异步模型

  • OpenHarmony标准系统异步接口实现支持Promise方式和Callback方式。NAPI支持异步模型,提供了Promise、Callback方式。
  • 标准系统异步接口实现规范要求,若引擎开启Promise特性支持,则异步方法必须同时支持Callback方式和Promise方式。
    • 由应用开发者决定使用哪种方式,通过是否传递Callback函数区分异步方法是Callback方式还是Promise方式
    • 不传递Callback即为Promise方式(方法执行结果为Promise实例对象),否则为Callback方式
  • Promise、Callback 异步模型都是 OHOS 标准异步模型。
  • Callback异步模型
    • 用户在调用接口的时候,接口实现将异步执行任务
    • 任务执行结果以参数的形式提供给用户注册的回调函数,这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。
  • Promise异步模型
    • 对象的状态不受外界影响;
    • 一旦状态改变了就不会再变,也就是说任何时候Promise都只有一种状态。
  • ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案回调函数和事件;
    • promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的;
    • 用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果;
    • 在返回的结果的个数超过一个时,其以对象属性的形式返回。
  • ES6:全称ECMAScript 6.0。ECMAScript 是JavaScript语言的国际标准,JavaScript是ECMAScript的实现。

Callback 异步接口

Callback 异步接口示例代码

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传入参数在异步工作项工作时序图中位置,在图中用红框标记如下

  // 获取并判断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;
  }
  • 使用napi_typeof接口进行参数类型的判断
  • NAPI_CALL()是用来调用NAPI中的API的。

主线程:初始化上下文数据

初始化上下文数据在异步工作项工作时序图中位置,在图中用红框标记如下

  • 异步方法需要在不同线程中传递各种业务数据上下文数据),就需要定义一个结构体保存这些被传递的信息。用于在主线程方法、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(返回值)
};
  • OpenHarmony的NAPI框架将ECMAScript标准中定义的Boolean、Null、Undefined、Number、BigInt、String、Symbol和Object八种数据类型Function类型,都已统一封装为napi_value类型,故可如获取数据类型的参数一样获取Function类型的参数。

Function是JavaScript提供的一种引用类型,通过Function类型创建Function对象。
在JavaScript中,函数也是以对象的形式存在的,每个函数都是一个Function对象。

  • 定义好结构体后,接着我们将接收到的3个参数(加数被加数回调函数)转换存入上下文数据完成初始化上下文数据
    • number类型的(加数被加数)转换为double直接存入。
    • Function类型的参数(回调函数)怎么处理?不能直接存入napi_value类型。
      • 因为牵涉到NAPI对象生命周期管理问题。napi_value类型引用对象的生命周期在原生方法退出后结束,后面在work线程无法获取其值。
      • NAPI提供了一种生命期限长于原生方法的对象引用类型—— napi_ref,所以调用napi_create_reference()函数将接收到的napi_value类型的回调函数参数callback转换为napi_ref类型。napi_create_reference()函数定义如下:
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_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创建一个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) 
  ...
}
  • NAPI_CALL()是用来调用NAPI中的API的。

主线程:创建异步工作项

创建异步工作项在异步工作项工作时序图中位置,在图中用红框标记如下

  • 第一步:在创建异步工作项前,分别声明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参数。
  • 第二步:利用NAPI框架提供的napi_create_async_work()函数创建异步工作项,将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是上下文数据中创建的异步工作对象,用于管理异步工作线程。
  ...
}

主线程:原生方法返回临时返回值

  • 调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回空值退出。

  • 用户在调用接口的时候,接口实现将异步执行任务,任务执行结果以参数的形式提供给用户注册的回调函数(callback),这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。

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;
}

work线程:执行业务逻辑、把计算结果写入上下文数据

执行业务逻辑把计算结果写入上下文数据在异步工作项工作时序图中位置,在图中用红框标记如下

创建异步工作项前,声明了addExecuteCB这个函数,用作于napi_create_async_work()函数的execute参数。

  • execute函数在异步工作项被调度后在work线程中执行

    • 不阻塞主线程(不阻塞UI界面)
    • 可执行IO、CPU密集型等任务。
  • 执行业务逻辑:业务逻辑计算是一个简单的加法,并把计算结果存入上下文数据的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];
}

EventLoop线程:把上下文中的结果转为JS类型、调用JS回调函数

把上下文中的结果转为JS类型调用JS回调函数在异步工作项工作时序图中位置,在图中用红框标记如下

  • 创建异步工作项前,声明addAsyncCompleteCB这个函数,用作于napi_create_async_work()函数的complete参数。
    • 第一步:addAsyncCompleteCB从接收到的上下文数据中获取结果,调用napi_call_function()方法执行JS回调函数返回数据给JS。
    • 第二步: 释放(删除)过程中创建的napi_ref引用对象、异步工作项等对象。
// 业务逻辑处理完成回调函数,在业务逻辑处理函数执行完成或取消后触发,由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框架提供了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表示转换成功,其他值失败。

  • 执行回调函数是为了在异步操作之后调用JS函数

  • napi_delete_reference()用于删除上下文数据中定义的napi_ref对象callback。napi_ref引用对象在原生方法退出后不自动回收,由用户管理napi_ref类型对象的生命周期。

  • napi_delete_async_work()用于删除异步工作线程,在异步调用的结尾释放async_work和相关业务数据的内存

Callback异步接口总结

以下图片为个人总结,可以在文末下载清晰的图片,下载之后推荐到[diagrams]。

Promise异步接口

Promise异步接口示例代码

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;

创建Promise

  • Promise整体处理流程和Callback方式一样,在此小节只讨论Promise不同于Callback的部分

  • 首先创建Promise,NAPI框架中提供了napi_create_promise()函数用于创建Promise,调用该函数输出2个对象——deferred、promise。

    • promise用于原生方法返回,deferred传入异步工作项的上下文数据。complete函数中,应用napi_resolve_deferred()函数 或 napi_reject_deferred() 函数返回数据。

函数定义如下:

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;
}

初始化上下文数据

  • 定义一个上下文数据结构,用于保存和传递数据。Promise方式加上deferred属性。
// 用户提供的上下文数据,在原生方法(初始化数据)、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]));
  ...
}
  • Callback方式在addCallback传入的是三个参数,Promise方式在addPromise传入的是两个参数。

创建异步工作项

  • 同Callback方式一样在创建异步工作项前,分别声明2个函数,分别用作于napi_create_async_work()函数的execute、complete参数。

  • 异步工作项创建OK后,将其存入上下文数据的asyncWork属性,并调用napi_queue_async_work()将异步工作项加入调度队列,由异步work线程池统一调度,原生方法返回Promise对象退出。

// 用户提供的上下文数据,在原生方法(初始化数据)、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;
}

execute 回调处理

此处完全同Callback方式,无需修改。

// 业务逻辑处理函数,由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = (AddonData *)data;

  // 执行复杂计算,不阻塞主线程。此处用一个加法简单示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

complete 回调处理

  • 调用NAPI提供的napi_resolve_deferred() 或 napi_reject_deferred() 返回数据。之后释放过程中创建的napi_ref引用对象、异步工作项等对象。
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;

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

同步方法

  • 同步方法调用之后,将阻塞住JS线程直至获取到返回值。

  • 命名:动词+Sync或动词+名词+Sync

  • 格式:

    • 无参:方法名()
    • 有参:方法名Sync(必填参数[, 可选参数])
  • 返回值:有

  • 声明文件模板

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;

异步方法

  • 异步方法调用整个过程不会阻碍调用者的工作。

  • 命名:动词或动词+名词

  • 格式:

    • 无参:方法名([回调函数])
    • 有参:方法名(必填参数[, 可选参数][, 回调函数])
  • 返回值

    • 若回调函数非空,则返回void
    • 若回调函数为空,则返回Promise实例对象
  • 声明文件模板

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;	

NAPI中的数据类型

  • NAPI使用的数据类型和Node.js N-API保持一致。OpenHarmony的NAPI(Native API)组件是一套对外接口基于Node.js N-API规范开发的原生模块扩展开发框架。
    通过查看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 struct napi_env__* napi_env为例,搜遍Node.js的源码都找不到napi_value__定义,那这个定义是什么意思呢?c语言中,允许定义一个没有定义的结构体的指针。所以napi_value其实就是一个一级指针。他不需要类型信息。

typedef作用就是定义类型别名

关于NAPI标准库中导出的符号列表

  • NAPI它基于Node.js N-API规范开发,因此可参考Node.js N-API了解NAPI标准库中符号列表。本文以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

  • 标准库中导出的符号列表

符号类型符号名备注
FUNCnapi_module_register
FUNCnapi_get_last_error_info
FUNCnapi_throw
FUNCnapi_throw_error
FUNCnapi_throw_type_error
FUNCnapi_throw_range_error
FUNCnapi_is_error
FUNCnapi_create_error
FUNCnapi_create_type_error
FUNCnapi_create_range_error
FUNCnapi_get_and_clear_last_exception
FUNCnapi_is_exception_pending
FUNCnapi_fatal_error
FUNCnapi_open_handle_scope
FUNCnapi_close_handle_scope
FUNCnapi_open_escapable_handle_scope
FUNCnapi_close_escapable_handle_scope
FUNCnapi_escape_handle
FUNCnapi_create_reference
FUNCnapi_delete_reference
FUNCnapi_reference_ref
FUNCnapi_reference_unref
FUNCnapi_get_reference_value
FUNCnapi_create_array
FUNCnapi_create_array_with_length
FUNCnapi_create_arraybuffer
FUNCnapi_create_external
FUNCnapi_create_external_arraybuffer
FUNCnapi_create_object
FUNCnapi_create_symbol
FUNCnapi_create_typedarray
FUNCnapi_create_dataview
FUNCnapi_create_int32
FUNCnapi_create_uint32
FUNCnapi_create_int64
FUNCnapi_create_double
FUNCnapi_create_string_latin1
FUNCnapi_create_string_utf8
FUNCnapi_get_array_length
FUNCnapi_get_arraybuffer_info
FUNCnapi_get_prototype
FUNCnapi_get_typedarray_info
FUNCnapi_get_dataview_info
FUNCnapi_get_value_bool
FUNCnapi_get_value_double
FUNCnapi_get_value_external
FUNCnapi_get_value_int32
FUNCnapi_get_value_int64
FUNCnapi_get_value_string_latin1
FUNCnapi_get_value_string_utf8
FUNCnapi_get_value_uint32
FUNCnapi_get_boolean
FUNCnapi_get_global
FUNCnapi_get_null
FUNCnapi_get_undefined
FUNCnapi_coerce_to_bool
FUNCnapi_coerce_to_number
FUNCnapi_coerce_to_object
FUNCnapi_coerce_to_string
FUNCnapi_typeof
FUNCnapi_instanceof
FUNCnapi_is_array
FUNCnapi_is_arraybuffer
FUNCnapi_is_typedarray
FUNCnapi_is_dataview
FUNCnapi_is_date
FUNCnapi_strict_equals
FUNCnapi_get_property_names
FUNCnapi_set_property
FUNCnapi_get_property
FUNCnapi_has_property
FUNCnapi_delete_property
FUNCnapi_has_own_property
FUNCnapi_set_named_property
FUNCnapi_get_named_property
FUNCnapi_has_named_property
FUNCnapi_set_element
FUNCnapi_get_element
FUNCnapi_has_element
FUNCnapi_delete_element
FUNCnapi_define_properties
FUNCnapi_call_function
FUNCnapi_create_function
FUNCnapi_get_cb_info
FUNCnapi_get_new_target
FUNCnapi_new_instance
FUNCnapi_define_class
FUNCnapi_wrap
FUNCnapi_unwrap
FUNCnapi_remove_wrap
FUNCnapi_create_async_work
FUNCnapi_delete_async_work
FUNCnapi_queue_async_work
FUNCnapi_cancel_async_work
FUNCnapi_get_node_version
FUNCnapi_get_version
FUNCnapi_create_promise
FUNCnapi_resolve_deferred
FUNCnapi_reject_deferred
FUNCnapi_is_promise
FUNCnapi_run_script
FUNCnapi_get_uv_event_loop

Native API接口说明

符号类型符号名备注
FUNCnapi_run_script_path运行JavaScript文件

关于镜像文件的编译

  • 初次编译OpenHarmony标准系统镜像时,会完整的编译出boot_linux.imgconfig.cfgMiniLoaderAll.binparameter.txtramdisk.imgresource.imgsystem.imguboot.imgupdater.imguserdata.imgvendor.img文件
  • 后面自己修改源码(不涉及内核源码)后编译,可以烧录自己编译的system.imgvendor.imgupdater.imguserdata.imgramdisk.img

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/538511.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法每日一练]-图论(lca) 最近公共祖先LCA,货车运输

目录 P3379&#xff1a;最近公共祖先LCA 思路&#xff1a; 货车运输 P3379&#xff1a;最近公共祖先LCA 思路&#xff1a; 首先进行的预处理&#xff0c;将所有点的深度和p数组求出来 设置&#xff1a;p[i][j]存的从i向上走2的j次方那么长的路径到的父节点 更深的点走到和…

STM32学习和实践笔记(8): 理解位带区和位带别名区

如前《STM32学习和实践笔记&#xff08;4&#xff09;: 分析和理解GPIO_InitTypeDef GPIO_InitStructure (b)&#xff08;含memory mapping图&#xff09;-CSDN博客 》中所写&#xff0c; STM32一共有4GB的地址&#xff0c;对所有的寄存器、存储器、外设等进行统一编址。 每…

EaticSearch学习

ES学习目标 1、全文检索 2、ES介绍 2.1 安装&#xff08;docker&#xff09; docker pull elasticsearch:7.14.0 docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.typesingle-node" elasticsearch:7.14.0初步检索 1、_cat GET /_cat/nodes&#xff1a;查看所…

MySQL选择普通索引还是唯一索引(2/16)

普通索引和唯一索引 基本概述 MySQL中可以创建普通索引与唯一索引&#xff0c;这两种索引的区别是&#xff1a; 普通索引&#xff08;Non-Unique Index&#xff09;&#xff0c;也称为非唯一索引&#xff0c;它允许索引中的条目具有重复的键值。普通索引的主要目的是加快查询…

HarmonyOS开发学习:【DevEco Device Tool 安装配置(问题全解)】

本文介绍如何在Windows主机上安装DevEco Device Tool工具。 坑点总结&#xff1a; 国内部分网络环境下&#xff0c;安装npm包可能会很慢或者超时&#xff0c;推荐使用国内npm源&#xff08;如淘宝源、华为源等&#xff09;&#xff1b;serialport这个npm包安装的过程中需要编…

暴力枚举法

虽然暴力枚举法有时候效率低&#xff0c;时间复杂度高&#xff0c;但是在面对小规模数据集的时候&#xff0c;暴力枚举法往往是很好的思维利器。 B: 01 串的熵&#xff08;5分&#xff09; 问题描述 #include <iostream> #include <cmath> #include <algorithm…

第二十六周代码(总结 + 查缺补漏)

蓝桥云课&#xff1a;刷题数量通过139题&#xff0c;尝试解决&#xff08;未做出&#xff09;18题。 其中蓝桥杯往年真题74题&#xff0c;尝试解决&#xff08;未做出&#xff09;6题算法模板题5题经典算法题20题&#xff0c;尝试解决&#xff08;未做出&#xff09;1题算法赛…

从零自制docker-9-【管道实现run进程和init进程传参】

文章目录 命令行中输入参数长度过长匿名管道从父进程到子进程传参[]*os.File{}os.NewFile和io.ReadAllexe.LookPathsyscall.Execstrings.Split(msgStr, " ")/bin/ls: cannot access : No such file or directory代码 命令行中输入参数长度过长 用户输入参数过长或包…

红黑瓷砖(BFS和DFS)

9 6 ....#. .....# ...... ...... ...... ...... ...... #...# .#..#.45BFS import java.util.Deque; import java.util.LinkedList; import java.util.Scanner;public class Main {//. 黑色//# 红色// 黑色开始static final int N 11;static int n,m,ans 1; static char[][…

传统文字检测方法+代码实现

文章目录 前言传统文字检测方法1、基于最大稳定极值区域&#xff08;MSER&#xff09;的文字检测1.1 MSER&#xff08;MSER-Maximally Stable Extremal Regions&#xff09;基本原理代码实现——使用Opencv中的cv2.MSER_create()接口 2、基于笔画宽度变换&#xff08;Stroke Wi…

【示例】MySQL-SQL语句优化

前言 本文主要讲述不同SQL语句的优化策略。 SQL | DML语句 insert语句 插入数据的时候&#xff0c;改为批量插入 插入数据的时候&#xff0c;按照主键顺序插入 大批量插入数据的时候&#xff08;百万&#xff09;&#xff0c;用load指令&#xff0c;从本地文件载入&#x…

基于深度学习的人脸表情识别系统(PyQT+代码+训练数据集)

基于深度学习的人脸表情识别系统&#xff08;PyQT代码训练数据集&#xff09; 前言一、数据集1.1 数据集介绍1.2 数据预处理 二、模型搭建三、训练与测试3.1 模型训练3.2 模型测试 四、PyQt界面实现 前言 本项目是基于mini_Xception深度学习网络模型的人脸表情识别系统&#x…

关于nvm node.js的按照

说明&#xff1a;部分但不全面的记录 因为过程中没有截图&#xff0c;仅用于自己的学习与总结 过程中借鉴的优秀博客 可以参考 1,npm install 或者npm init vuelatest报错 2&#xff0c;了解后 发现是nvm使用的版本较低&#xff0c;于是涉及nvm卸载 重新下载最新版本的nvm 2…

4月12日重新安排行程

332.重新安排行程 332. 重新安排行程 - 力扣&#xff08;LeetCode&#xff09; 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机…

Linux网络 基础概念

目录 背景知识 互联网的发展 局域网和广域网 网络拓扑 网络协议栈 协议的概念 网络协议的分层 网络与操作系统的联系 网络传输的基本流程 IP地址和MAC地址 以太网通信 数据包的封装和分用 跨网段传输 背景知识 互联网的发展 计算机网络是计算机技术和通信技术相…

循环新蓝海,“新”从“旧”中来

浙江安吉&#xff0c;是“两山”理念——“绿水青山就是金山银山”的发源地&#xff0c;也是众多循环经济和绿色产业的根据地。这里汇集了大批已上市和待上市的相关公司的总部&#xff0c;年初刚递表港交所的闪回科技&#xff0c;就是其中之一。 主营二手手机回收和销售的闪回…

卫星图像10个开源数据集资源汇总

文章目录 1、UC Merced Land-Use 2、Indian Pines 3、KSC 4、Washington DC 5、BigEarthNet 6、水体卫星图像的图像 7、城市航拍图像分割数据集 8、游泳池和汽车卫星图像检测 9、人工月球景观数据集 10、马萨诸塞州道路数据集 1、UC Merced Land-Use 数据集下载地址&am…

一文看懂交易主机托管!(此篇足矣)

什么是主机托管&#xff1f; 主机托管的类型&#xff1f; 如何开通主机托管&#xff1f; 主机托管的费用? 日常大家关心最多的就是这几个问题&#xff01;小编今天我们全面一次型解答&#xff01;帮助我们跟多了解&#xff01;建议收藏&#xff0c;避免下次找不到了哦&#x…

VulnHub靶机-easy_cloudantivirus 打靶

easy_cloudantivirus 靶机 目录 easy_cloudantivirus 靶机一、导入虚拟机配置二、攻击方式主机发现端口扫描web渗透-SQL注入命令注入反弹shellssh爆破提权 一、导入虚拟机配置 靶机地址&#xff1a; https://www.vulnhub.com/entry/boredhackerblog-cloud-av,453/下载完成&am…

DOTS Instancing合批:如何针对单个渲染实体修改材质参数

最近在做DOTS的教程,由于DOTS(版本1.0.16)目前不支持角色的骨骼动画&#xff0c;我们是将角色的所有动画数据Baker到一个纹理里面&#xff0c;通过修改材质中的参数AnimBegin,AnimEnd来决定动画播放的起点和终点&#xff0c;材质参数AnimTime记录当前过去的动画时间。但是在做大…