OpenHarmony 源码解析之JavaScript API框架(NAPI)

网友投稿 349 2023-05-22

[[424375]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

1.NAPI概念

1.1 JS API概念

JS API: JavaScript Application Programming Interface, JavaScript应用程序编程接口。

1.2 JS API实现方式

OpenHarmony上JS API实现方式有三种,分别是:JSI机制、Channel机制、NAPI机制。

JSI机制:L0~L1设备支持。

Channel机制:L3设备支持。

NAPI机制:目前仅L2设备支持,后续须推广到L3~L5设备。

1.3 NAPI概念

一句话概括NAPI,就是L2设备上的 JS API实现方式。

2.NAPI机制介绍

2.1 实现原则

优先封装异步方法!同步方法可待社区反馈需要时再行添加。

若引擎开启Promise特性支持,则异步方法必须同时支持Callback方式和Promise方式。使用哪种方式由应用开发者决定,以是否传递Callback进行区分。不传递Callback即为Promise方式,方法执行结果为Promise实例对象。

L0到L1设备上受限于硬件水平,只实现Callback方式的异步方法; L2到L5设备上,必须实现同时支持Callback方式和Promise方式的异步方法。

2.2 异步编程模型

2.2.1Promise 异步模型

Promise 异步模型是 OHOS 标准异步模型之一。

Promise对象: ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案–回调函数和事件。promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的。用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果。在返回的结果的个数超过一个时,其以对象属性的形式返回。

Promise特点 作为对象,Promise有两个特点:(1)对象的状态不受外界影响;(2)一旦状态改变了就不会再变,也就是说任何时候Promise都只有一种状态。

2.2.2Callback 异步模型

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

2.2 实现步骤

2.2.1 模块注册

API集合按业务功能进行模块划分。开发者使用前须import对应的模块。

命名:@ohos.模块名

注意:

模块名须唯一,由ACE团队统一维护,子系统新增模块时须向ACE团队申请。 模块名最好是单个名词。实在不行,也可以由多个名词组成,但必须遵循小驼峰命名规则。 一个模块,一个声明文件(*.d.ts)。声明文件命名遵循@ohos.模块名.d.ts,文件名全小写,单词间无分割。

N-API通过注册函数进行模块的注册,其接受一个全局变量参数,全局变量结构体中定义了模块名及模块初始化函数。在模块的初始化中,我们可以定义模块需要暴露的方法及属性。

示例:

复制static napi_value StorageExport(napi_env env, napi_value exports)  const char* storageClassName = "Storage" napi_value storageClass = nullptr;  /* 定义模块需要对外暴露的方法 */  static napi_property_descriptor storageDesc[] = {      DECLARE_NAPI_FUNCTION("get", JSStorageGet),      DECLARE_NAPI_FUNCTION("getSync", JSStorageGetSync),  };  /* 定义C++类对应的JavaScript类,包括JS类名、JS构造函数 */  napi_define_class(env, storageClassName, strlen(storageClassName), JSStorageConstructor, nullptr,                    sizeof(storageDesc) / sizeof(storageDesc[0]), storageDesc, &storageClass);  /* 定义模块需要对外暴露的属性 */  static napi_property_descriptor desc[] = {      DECLARE_NAPI_PROPERTY("Storage", storageClass),  };  /* 设置exports对象属性 */  napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);  return exports;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.

模块定义

复制static napi_module storageModule = {      .nm_version = 1,      .nm_flags = 0,      .nm_filename = nullptr,      .nm_register_func = StorageExport,      .nm_modname = "storage"     .nm_priv = ((void*)0),      .reserved = { 0 },      };  1.2.3.4.5.6.7.8.9.

 模块注册

复制extern "C" __attribute__((constructor)) void StorageRegister()      napi_module_register(&storageModule);  1.2.3.4.

2.2.2 NAPI声明

声明文件模板

@ohos.模块名.d.ts文件:

复制/**  * 模块描述  * @since API版本号,IT Release3 对应 4,以此类推  * @sysCap 系统能力  * @devices 支持设备  * @import 导入模块  * @permission 权限列表  */  declare namespace 模块名 {  // 在此处定义功能方法  export default 模块名;  1.2.3.4.5.6.7.8.9.10.11.12.13.

示例:

声明文件@ohos.storage.d.ts

复制/**   * 存储   * @since 3   * @sysCap ACE Engine   * @devices phone, tablet, tv, wearable, liteWearable, smartVision   * @import import storage from@ohos.storage  * @permission N/A   */  declare namespace storage {    // 在此处定义功能方法  export default storage;  1.2.3.4.5.6.7.8.9.10.11.12.13.

2.2.2 NAPI实现

JS API 调用流程如下图所示:

接口定义

复制 /**入参**        napi_env:表示一个上下文的变量;      napi_callback_info:传递给回调函数的一个封装的数据类型,可以用于获取有关调用时的上下文信息,也可以用于设置回调函数的返回值;   **返回值**       napi_value:对所有js的基本值的一个密闭封装,就是表示一个基本值;   */  static napi_value Get(napi_env env, napi_callback_info info);  static napi_value GetSync(napi_env env, napi_callback_info info);  1.2.3.4.5.6.7.8.9.10.

2.2.2.1 同步回调

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

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

格式:

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

返回值

声明文件模板

复制declare namespace 模块名      /**     * 方法描述     * @note 特殊说明     * @since (可选,方法支持版本与模块不一致时需标明)     * @sysCap 系统能力     * @devices 支持设备 (可选,支持设备类型与模块不一致时需标明)      * @param 参数 参数说明(可选,没有参数或参数用interface包含时不需要标明)     * @return 返回值说明(可选,没有返回值或返回值用interface包含时不需要标明)      */     // 无参     function 方法名Sync(): 返回值类型;      // 有参      function 方法名Sync(必填参数: 参数类型, options?: 可选参数类型): 返回值类型;      interface 可选参数类型 {     参数名: 参数类型;      }  export default 模块名;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

示例

声明

复制declare namespace storage {    /**     * getSync方法描述     * @since 6     * @sysCap ACE Enginge     * @param keykey值说明     * @return 返回值说明     */    function getSync(key: string,  options?: GetStorageOptions): string;    interface GetStorageOptions {      /**       * default参数描述       * @since 6       * @sysCap ACE Enginge       */      default: string;    }  export default storage;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.

实现

复制static napi_value GetSync(napi_env env, napi_callback_info info)                  {                     size_t requireArgc = 1;              size_t argc = 2; //参数个数              napi_value argv[2] = { 0 }; //参数定义              napi_value thisVar = nullptr; //JS对象的this参数              void* data = nullptr; //回调数据指针              /* 根据环境变量获取参数 */              napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);              NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");              charkey[KEY_BUFFER_SIZE] = { 0 };              size_t keyLen = 0;              char value[VALUE_BUFFER_SIZE] = { 0 };              size_t valueLen = 0;              for (size_t i = 0; i < argc; i++) {                  napi_valuetype valueType = napi_undefined;                  napi_typeof(env, argv[i], &valueType);                    if (i == 0 && valueType == napi_string) {                      /* 根据JS字符串获取对应的UTF8编码格式的C/C++字符串 */                      napi_get_value_string_utf8(env, argv[i], key, KEY_BUFFER_SIZE, &keyLen);                  } else if (i == 1 && valueType == napi_string) {                      napi_get_value_string_utf8(env, argv[i], value, VALUE_BUFFER_SIZE, &valueLen);                      break;                  } else {                      NAPI_ASSERT(env, false"type mismatch");                  }              }              StorageObjectInfo* objectInfo = nullptr;              /* 根据JS对象获取与之绑定的原生对象实例 */              napi_unwrap(env, thisVar, (void**)&objectInfo);              auto itr = g_keyValueStorage.find(key);              napi_value result = nullptr; // JS字符串对象              if (itr != g_keyValueStorage.end()) {                  /* 根据UTF8编码格式的 C/C++字符串 创建一个 JS字符串对象 */                  napi_create_string_utf8(env, itr->second.c_str(), itr->second.length(), &result);              } else if (valueLen > 0) {                  napi_create_string_utf8(env, value, valueLen, &result);              } else {                  objectInfo->Emit(nullptr, "error");                  NAPI_ASSERT(env, false"key does not exist");              }              return result; //返回JS对象          }  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.

2.2.2.2 异步回调

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

命名:动词或动词+名词

格式:

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

返回值

若回调函数非空,则返回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 模块名;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.

示例:

声明

复制import { AsyncCallback } from./basic declare namespace storage {  /**  * get方法描述  * @note N/A  * @since 5  * @sysCap ACE Engine  * @devices phone, tablet, tv, wearable  * @param keykey值说明  */  function get(key: string, callback: AsyncCallback<string>): void;  function get(key: string, options: GetStorageOptions, callback: AsyncCallback<string>): void;  function get(key: string, options?: GetStorageOptions): Promise<string>;  interface GetStorageOptions {  default: string;  export default storage;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.

实现

异步回调流程如下图所示:

复制static napi_value Get(napi_env env, napi_callback_info info)      size_t requireArgc = 1;      size_t argc = 3; //参数个数      napi_value argv[3] = { 0 }; //参数定义      napi_value thisVar = nullptr; //JS对象的this参数      void* data = nullptr; //回调数据指针      /* 根据环境变量获取参数 */      napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);      NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");      /* 异步接口上下文,用于接收JS接口传进来的环境变量、参数、回调函数、接口返回值等*/      auto asyncContext = new StorageAsyncContext();      asyncContext->env = env;      for (size_t i = 0; i < argc; i++) {          napi_valuetype valueType = napi_undefined;          napi_typeof(env, argv[i], &valueType);          if ((i == 0) && (valueType == napi_string)) {              /* 根据JS字符串获取对应的UTF8编码格式的C/C++字符串 */              napi_get_value_string_utf8(env, argv[i], asyncContext->key, KEY_BUFFER_SIZE, &asyncContext->keyLen);          } else if (valueType == napi_string) {              napi_get_value_string_utf8(env, argv[i], asyncContext->value, VALUE_BUFFER_SIZE, &asyncContext->valueLen);          } else if (valueType == napi_function) {              /* 根据JS对象参数argv[i]新建引用 */              napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);              break;          } else {              NAPI_ASSERT(env, false"type mismatch");          }      }      napi_value result = nullptr;      if (asyncContext->callbackRef == nullptr) {          /* Promise方式异步调用,创建延迟对象、JS Promise对象,使二者进行关联 */          napi_create_promise(env, &asyncContext->deferred, &result);      } else {          /* Callback方式异步调用,不需要返回Promise对象,返回一个JS未定义值 */          napi_get_undefined(env, &result);      }      /* 根据JS对象获取与之绑定的原生对象实例 */      napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);      napi_value resource = nullptr;      napi_create_string_utf8(env, "JSStorageGet", NAPI_AUTO_LENGTH, &resource); //获取JS异步资源名称      /* 创建异步工作 */      napi_create_async_work(          env, nullptr, resource,          /* 执行异步逻辑的原生函数 */          [](napi_env env, void* data) {              StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;              auto itr = g_keyValueStorage.find(asyncContext->key);              if (itr != g_keyValueStorage.end()) {                  if (strncpy_s(asyncContext->value, VALUE_BUFFER_SIZE, itr->second.c_str(), itr->second.length()) ==                      -1) {                      asyncContext->status = 1; //失败                  } else {                      asyncContext->status = 0; //成功                  }              } else {                  asyncContext->status = 1; //失败              }          },          /* 异步函数执行完成或者取消后,需要执行的后处理函数 */          [](napi_env env, napi_status status, void* data) {              StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;              napi_value result[2] = { 0 };              if (!asyncContext->status) {                  napi_get_undefined(env, &result[0]);                  napi_create_string_utf8(env, asyncContext->value, strlen(asyncContext->value), &result[1]);              } else {                  napi_value message = nullptr;                  napi_create_string_utf8(env, "key does not exist", NAPI_AUTO_LENGTH, &message);                  napi_create_error(env, nullptr, message, &result[0]);                  napi_get_undefined(env, &result[1]);                  asyncContext->objectInfo->Emit(nullptr, "error");              }              if (asyncContext->deferred) {                  if (!asyncContext->status) {                      /* 异步函数执行成功后,执行成功后处理函数 */                      napi_resolve_deferred(env, asyncContext->deferred, result[1]);                   } else {                      /* 异步函数执行失败后,执行失败后处理函数 */                      napi_reject_deferred(env, asyncContext->deferred, result[0]);                  }              } else {                  napi_value callback = nullptr;                  napi_get_reference_value(env, asyncContext->callbackRef, &callback);                  napi_call_function(env, nullptr, callback, sizeof(result) / sizeof(result[0]), result, nullptr);                  napi_delete_reference(env, asyncContext->callbackRef);              }              /* 异步回调完成后进行资源释放 */              napi_delete_async_work(env, asyncContext->work);              delete asyncContext;          },          /* 用户数据上下文,此数据传递给异步执行函数与后处理函数 */          (void*)asyncContext,          /* 生成的异步工作*/           &asyncContext->work);      napi_queue_async_work(env, asyncContext->work); //异步工作入队列,排队执行      return result;  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.

3. 应用代码示例

JS应用引用NAPI接口时,须先引用接口定义的对应模块,才能进行接口的调用。

复制import storage from@ohos.storage export default {       testGetSync() {          //同步接口            var name = storage.getSync(name);                   console.log(name is  + name);       },       testGet() {           //异步接口          storage.get(name) .then(date => console.log(name is  + data) ) .catch(error => console.log(error:  + error) );       }   }   1.2.3.4.5.6.7.8.9.10.11.12.13.

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:常用API大全分享!赶紧收藏起来!
下一篇:为什么需要银行卡文字识别API?让我来告诉你!
相关文章

 发表评论

暂时没有评论,来抢沙发吧~