作者:赵军霞
1 简介
JS API: JavaScript Application Programming Interface,JavaScript应用程序编程接口。
NAPI: 一句话概括NAPI,就是L2设备上的 JS API实现方式。
1.1 UI架构相关系列
【OpenHarmony 源码解析之ACE (JavaScript运行环境初始化)】
【OpenHarmony 源码解析之JavaScript API框架(NAPI)】
【OpenHarmony 源码解析之JavaScript(文件管理API)】
1.2 OpenHarmony架构图

1.3 JS UI架构
JS UI框架包括应用层(Application)、前端框架层(Framework)、引擎层(Engine)和平台适配层(Porting Layer),其架构如下图所示:


1.4 NAPI架构
NAPI机制属于JS UI框架中的前端框架层,其架构图如下:

1.5 JS API实现方式
OpenHarmony上JS API实现方式有两种,分别是:JSI机制、NAPI机制。
JSI机制:L0~L1设备支持。
NAPI机制:目前仅L2设备支持,后续须推广到L3~L5设备。
2 NAPI机制介绍
2.1 实现原则
- 优先封装异步方法!同步方法可待社区反馈需要时再行添加;
- 若引擎开启
Promise
特性支持,则异步方法必须同时支持Callback
方式和Promise
方式;
- 使用哪种方式由应用开发者决定,是否传递
Callback
进行区分。不传递Callback
即为Promise
方式,方法执行结果为Promise
实例对象;
L0
到L1
设备上受限于硬件水平,只实现Callback
方式的异步方法;
L2
到L5
设备上,必须实现同时支持Callback
方式和Promise
方式的异步方法。
2.2 异步编程模型
Promise、Callback 异步模型都是 OHOS 标准异步模型之一。
2.2.1 Promise 异步模型
Promise对象:
- ES6原生提供了Promise对象,Promise是异步编程的一种解决方案,可以替代传统的解决方案
回调函数和事件
;
- promise对象是一个异步操作的结果,提供了一些API使得异步执行可以按照同步的流表示出来,避免了层层嵌套的回调函数,保证了回调是以异步的方式进行调用的;
- 用户在调用这些接口的时候,接口实现将异步执行任务,同时返回一个 Promise 对象,其代表异步操作的结果;
- 在返回的结果的个数超过一个时,其以对象属性的形式返回。
Promise特点: 作为对象,Promise有两个特点:
2.2.2 Callback 异步模型
-
用户在调用这些接口的时候,接口实现将异步执行任务;
-
任务执行结果以参数的形式提供给用户注册的回调函数;
-
这些参数的第一个是 Error 或 undefined 类型,分别表示执行出错与正常。
3 实现步骤
3.1 模块注册
API集合按业务功能进行模块划分。开发者使用前须import对应的模块。
-
命名:@ohos.模块名
-
注意:
- 模块名须唯一,由
ACE
团队统一维护,子系统新增模块时须向ACE
团队申请。
- 模块名最好是单个名词。实在不行,也可以由多个名词组成,但必须遵循
小驼峰
命名规则。
- 一个模块,一个声明文件(
*.d.ts
)。声明文件命名遵循@ohos.模块名.d.ts
,文件名全小写,单词间无分割。
N-API通过注册函数进行模块的注册,其接受一个全局变量参数,全局变量结构体中定义了模块名及模块初始化函数。在模块的初始化中,我们可以定义模块需要暴露的方法及属性。
3.2 NAPI声明
-
声明文件模板
@ohos.模块名.d.ts
文件:
-
示例:
声明文件@ohos.storage.d.ts
3.3 NAPI实现
JS API 调用流程如下图所示:

接口定义
3.4 同步回调
同步方法调用之后,将阻塞住JS线程直至获取到返回值。
-
命名:动词+Sync
或动词+名词+Sync
-
格式:
- 无参:
方法名()
- 有参:
方法名Sync(必填参数[, 可选参数])
-
返回值:有
-
声明文件模板
-
示例:
声明
实现
3.5 异步回调
异步方法调用整个过程不会阻碍调用者的工作。
-
命名:动词
或动词+名词
-
格式:
- 无参:
方法名([回调函数])
- 有参:
方法名(必填参数[, 可选参数][, 回调函数])
-
返回值
- 若
回调函数
非空,则返回void
- 若
回调函数
为空,则返回Promise
实例对象
-
声明文件模板
-
示例:
声明
实现-异步回调流程如下图所示:

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;
void* data = nullptr;
napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
NAPI_ASSERT(env, argc >= requireArgc, "requires 1 parameter");
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)) {
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) {
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) {
napi_create_promise(env, &asyncContext->deferred, &result);
} else {
napi_get_undefined(env, &result);
}
napi_unwrap(env, thisVar, (void**)&asyncContext->objectInfo);
napi_value resource = nullptr;
napi_create_string_utf8(env, "JSStorageGet", NAPI_AUTO_LENGTH, &resource);
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.
4 应用代码示例
JS应用引用NAPI接口时,须先引用接口定义的对应模块,才能进行接口的调用。
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
作者写的很详细,可以对NAPI流程有完整的认知,感谢作者分享
目前鸿蒙JS方面的开发文档和参考源码相对JAVA比较少,这个文章对于我这个JSER来说真的很有帮助。
终于可以一窥openharmony的NAPI机制了,nice
感谢分享,受益良多
这个专栏好文不断啊~ 棒棒哒~
原来 OpenHarmony 是用这种方法提供上层 JS 接口的, 豁然开朗~
感谢博主分享~
目录结构清晰,条理清楚,讲解到位。感谢分享
寫的太詳細啦,感謝分享。
结构清晰,层次分明,详细!感谢分享
这是港台小兄弟来捧场了?
厉害,不过作为上层应用开发,不是看的很懂C++源码555
支持~ 学习~
感谢认可,可以关注专栏,后期还有更多的优秀内容
感谢认可,可以关注专栏,后期还有更多的优秀内容
感谢认可,可以关注专栏,后期还有更多的优秀内容
感谢认可,可以关注专栏,后期还有更多的优秀内容
感谢认可,可以关注专栏,后期还有更多的优秀内容
感谢认可,可以关注专栏,后期还有更多的优秀内容
哈哈哈,观摩观摩,期待你的新作。
还有更多OpenHarmony源码解析,小伙伴们可以关注专栏哦