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 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.
实现
异步回调流程如下图所示:

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.