Native API在应用工程中的使用指导
HarmonyOS的应用必须用js来桥接native。需要使用napi接口来处理js交互。napi提供的接口名与三方Node.js一致,目前支持部分接口。
开发流程
在DevEco Studio的模板工程中包含使用Native API的默认工程,使用File->New->Create Project创建Native C++模板工程。创建后在main目录下会包含cpp目录,可以使用ace_napi仓下提供的napi接口进行开发。
js侧通过import引入native侧包含处理js逻辑的so,如:import hello from 'libhello.so',意为使用libhello.so的能力,native侧通过napi接口创建的js对象会给到应用js侧的hello对象。
开发建议
注册建议
- nm_register_func对应的函数需要加上static,防止与其他so里的符号冲突。
- 模块注册的入口,即使用__attribute__((constructor))修饰的函数的函数名需要确保不与其他模块重复。
so命名规则
- 每个模块对应一个so。
- 如模块名为hello,则so的名字为libhello.so,napi_module中nm_modname字段应为hello,大小写与模块名保持一致,应用使用时写作:import hello from 'libhello.so'。
js对象线程限制
ark引擎会对js对象线程使用进行保护,使用不当会引起应用crash,因此需要遵循如下原则:
- napi接口只能在js线程使用。
- env与线程绑定,不能跨线程使用。native侧js对象只能在创建时的线程使用,即与线程所持有的env绑定。
头文件引入限制
在使用napi的对象和方法时需要引用"napi/native_api.h"。否则在只引入三方库头文件时,会出现接口无法找到的编译报错。
napi_create_async_work接口说明
napi_create_async_work里有两个回调:
- execute:用于异步处理业务逻辑。因为不在js线程中,所以不允许调用napi的接口。业务逻辑的返回值可以返回到complete回调中处理。
- complete:可以调用napi的接口,将execute中的返回值封装成js对象返回。此回调在js线程中执行。
storage 模块——同步异步接口封装
模块简介
本示例通过实现 storage 模块展示了同步和异步方法的封装。storage 模块实现了数据的保存、获取、删除、清除功能。
接口声明
- 模块注册
如下,注册了4个同步接口(getSync、setSync、removeSync、clearSync)、4个异步接口(get、set、remove、clear)。
- getSync 函数实现
如上注册时所写,getSync 对应的函数是 JSStorageGetSync 。从 gKeyValueStorage 中获取数据后,创建一个字符串对象并返回。
- get 函数实现
如上注册时所写,get对应的函数式JSStorageGet。
static napi_value JSStorageGet(napi_env env, napi_callback_info info)
{
GET_PARAMS(env, info, 3);
NAPI_ASSERT(env, argc >= 1, "requires 1 parameter");
// StorageAsyncContext是自己定义的一个类,用于保存执行过程中的数据
StorageAsyncContext* asyncContext = new StorageAsyncContext();
asyncContext->env = env;
// 获取参数
for (size_t i = 0; i < argc; i++) {
napi_valuetype valueType;
napi_typeof(env, argv[i], &valueType);
if (i == 0 && valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], asyncContext->key, 31, &asyncContext->keyLen);
} else if (i == 1 && valueType == napi_string) {
napi_get_value_string_utf8(env, argv[i], asyncContext->value, 127, &asyncContext->valueLen);
} else if (i == 1 && valueType == napi_function) {
napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
break;
} else if (i == 2 && valueType == napi_function) {
napi_create_reference(env, argv[i], 1, &asyncContext->callbackRef);
} else {
NAPI_ASSERT(env, false, "type mismatch");
}
}
napi_value result = nullptr;
// 根据参数判断开发者使用的是promise还是callback
if (asyncContext->callbackRef == nullptr) {
// 创建promise
napi_create_promise(env, &asyncContext->deferred, &result);
} else {
napi_get_undefined(env, &result);
}
napi_value resource = nullptr;
napi_create_string_utf8(env, "JSStorageGet", NAPI_AUTO_LENGTH, &resource);
napi_create_async_work(
env, nullptr, resource,
// 回调1:此回调由napi异步执行,里面就是需要异步执行的业务逻辑。由于是异步线程执行,所以不要在此通过napi接口操作JS对象。
[](napi_env env, void* data) {
StorageAsyncContext* asyncContext = (StorageAsyncContext*)data;
auto itr = gKeyValueStorage.find(asyncContext->key);
if (itr != gKeyValueStorage.end()) {
strncpy_s(asyncContext->value, 127, itr->second.c_str(), itr->second.length());
asyncContext->status = 0;
} else {
asyncContext->status = 1;
}
},
// 回调2:此回调在上述异步回调执行完后执行,此时回到了JS线程来回调开发者传入的回调
[](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]);
}
if (asyncContext->deferred) {
// 如果走的是promise,那么判断回调1的结果
if (!asyncContext->status) {
// 回调1执行成功(status为1)时触发,也就是触发promise里then里面的回调
napi_resolve_deferred(env, asyncContext->deferred, result[1]);
} else {
// 回调1执行失败(status为0)时触发,也就是触发promise里catch里面的回调
napi_reject_deferred(env, asyncContext->deferred, result[0]);
}
} else {
// 如果走的是callback,则通过napi_call_function调用callback回调返回结果
napi_value callback = nullptr;
napi_value returnVal;
napi_get_reference_value(env, asyncContext->callbackRef, &callback);
napi_call_function(env, nullptr, callback, 2, result, &returnVal);
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.
- js示例代码
NetServer 模块——native与js对象绑定
模块简介
本示例展示了on/off/once订阅方法的实现,同时也包含了 C++ 与 js对象通过 wrap 接口的绑定。NetServer 模块实现了一个网络服务。
接口声明
1、模块注册
2、在构造函数中绑定 C++ 与 JS 对象
3、从 JS 对象中取出 C++ 对象
netServer->Start后回调通过on注册的start事件。
4、注册或释放(on/off/once)事件,以 on 为例
5、js示例代码
在非JS线程中回调JS接口
模块简介
本示例介绍如何在非JS线程中回调JS应用的回调函数。例如JS应用中注册了某个sensor的监听,这个sensor的数据是由一个SA服务来上报的,当SA通过IPC调到客户端时,此时的执行线程是一个IPC通信线程,与应用的JS线程是两个不同的线程。这时就需要将执行JS回调的任务抛到JS线程中才能执行,否则会出现崩溃。
1、模块注册
如下,注册了1个接口test,会传入一个参数,类型为包含一个参数的函数。
2、获取env中的loop,抛任务回JS线程
3、js示例代码
文章转载自:https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/napi-guidelines-0000001428061684-V3