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

开发 前端
JavaScript Application Programming Interface, JavaScript应用程序编程接口。OpenHarmony上JS API实现方式有三种,分别是:JSI机制、Channel机制、NAPI机制。

[[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设备。

OpenHarmony 源码解析之JavaScript API框架(NAPI)-鸿蒙HarmonyOS技术社区

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 调用流程如下图所示:

OpenHarmony 源码解析之JavaScript API框架(NAPI)-鸿蒙HarmonyOS技术社区

接口定义

 /**入参**     
  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 key key值说明 
   * @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"); 
             
            char key[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 key key值说明 
*/ 
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.

实现

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

OpenHarmony 源码解析之JavaScript API框架(NAPI)-鸿蒙HarmonyOS技术社区
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

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-07-19 20:04:31

NAPI模块鸿蒙

2021-11-25 09:54:54

鸿蒙HarmonyOS应用

2021-12-08 15:07:51

鸿蒙HarmonyOS应用

2021-12-06 06:19:03

鸿蒙HarmonyOS应用

2024-01-03 15:41:49

2021-08-30 18:09:57

鸿蒙HarmonyOS应用

2022-02-14 14:47:11

SystemUIOpenHarmon鸿蒙

2022-05-17 10:42:36

reboot源码解析

2022-06-13 14:18:39

电源管理子系统耗电量服务

2022-05-20 10:32:49

事件循环器事件队列鸿蒙

2021-11-08 15:04:47

鸿蒙HarmonyOS应用

2022-02-17 20:57:07

OpenHarmon操作系统鸿蒙

2021-12-17 16:42:09

鸿蒙HarmonyOS应用

2022-01-06 16:17:58

鸿蒙HarmonyOS应用

2022-07-05 16:03:29

电源管理子系统鸿蒙

2021-09-18 14:40:37

鸿蒙HarmonyOS应用

2022-01-20 14:33:29

openharmonwayland协议鸿蒙

2022-01-10 15:30:11

鸿蒙HarmonyOS应用

2023-04-12 15:31:11

系统服务管理鸿蒙

2023-03-17 07:53:20

K8sAPIServerKubernetes
点赞
收藏

51CTO技术栈公众号