harmony 鸿蒙安全和高效的使用N-API开辟Native模块_鸿蒙native开辟(1) ...

打印 上一主题 下一主题

主题 1030|帖子 1030|积分 3090



网上学习资料一大堆,但假如学到的知识不成体系,遇到标题时只是浅尝辄止,不再深入研究,那么很难做到真正的技能提升。
须要这份系统化的资料的朋友,可以戳这里获取
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都接待到场我们的的圈子(技能交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
  1. NULL,
  2. NULL,
  3. NULL,
  4. napi_enumerable|napi_static,
  5. NULL};
复制代码
NODE_API_CALL(env, napi_create_object(env, &return_value));
status = napi_define_class(NULL,
“TrackedFunction”,
NAPI_AUTO_LENGTH,
TestDefineClass,
NULL,
1,
&property_descriptor,
&result);
SaveConstructor(env, result);

}
  1. ---
复制代码
// 2、由开辟者自己管理 constructor 对象的生命周期
napi_status SaveConstructor(napi_env env, napi_value constructor) {
return napi_create_reference(env, constructor, 1, &g_constructor);
};
napi_status GetConstructor(napi_env env) {
napi_value constructor;
return napi_get_reference_value(env, g_constructor, &constructor);
};
  1. ##### 使用案例2:napi\_wrap
  2. 开发者使用 napi\_wrap 接口,可以将 native 对象和 js 对象绑定,当 js 对象被 GC 回收时,需要通过回调函数对 native 对象的资源进行清理。napi\_wrap 接口本质上也是创建了一个 napi\_ref,开发者可以根据业务需要,选择由系统来管理创建的 napi\_ref,或是自行释放创建的 napi\_ref。
复制代码
// 用法1:napi_wrap不须要吸收创建的napi_ref,末了一个参数传递nullptr,创建的napi_ref由系统管理,不须要用户手动开释
napi_wrap(env, jsobject, nativeObject, cb, nullptr, nullptr);
// 用法2:napi_wrap须要吸收创建的napi_ref,末了一个参数不为nullptr,返回的napi_ref须要用户手动开释,否则会内存走漏
napi_ref result;
napi_wrap(env, jsobject, nativeObject, cb, nullptr, &result);
// 当jsobject和result后续不再使用时,及时调用napi_remove_wrap开释result
napi_value result1;
napi_remove_wrap(env, jsobject, result1)
  1. ### 跨语言调用开销
  2. #### 接口调用
  3. 跨语言调用是指在一个程序中使用多种编程语言编写的代码,并且这些代码可以相互调用和交互,ArkTS 调用 C++ 就是一种跨语言调用的方式。使用 N-API 进行函数调用会引入一定的开销,因为需要进行上下文切换、参数传递、函数调用和返回值处理等,这些过程都涉及到一些性能开销。目前,通过 N-API 接口实现 ArkTS 调用 C++ 的场景大致分为三类:ArkTS 直接调用 C++ 接口、ArkTS 监听 C++ 接口以及 ArkTS 接收 C++ 回调。频繁的跨语言接口调用可能会影响业务性能,因此需要开发者合理的设计接口调用频率。
  4. #### 数值转换
  5. 使用 N-API 进行 ArkTS 与 C++ 之间的数据转换,有如下建议: \* 减少数据转换次数:频繁的数据转换可能会导致性能下降,可以通过批量处理数据或者使用更高效的数据结构来优化性能; \* 避免不必要的数据复制:在进行数据转换时,可以使用 N-API 提供的接口来直接访问原始数据,而不是创建新的数据副本; \* 使用缓存:如果某些数据在多次转换中都会被使用到,可以考虑使用缓存来避免重复的数据转换。缓存可以减少不必要的计算,提高性能。
  6. ### 异步操作
  7. 对于IO、CPU密集型任务需要异步处理, 否则会造成主线程的阻塞。N-API 支持异步能力,允许应用程序在执行某个耗时任务时不会被阻塞,而是继续执行其他任务。当异步操作完成时,应用程序会收到通知,并可以处理异步操作的结果。
  8. #### 异步示例
  9. 开发者可以通过如下示例将耗时任务用异步方式实现,大概逻辑包括以下三步: \* 用 napi\_create\_promise 接口创建 promise,将创建一个 deferred 对象并与 promise 一起返回,deferred 对象会绑定到已创建的 promise; \* 执行耗时任务,并将执行结果传递给 promise; \* 使用 napi\_resolve\_deferred 或 napi\_reject\_deffered 接口来 resolve 或 reject 创建的 promise,并释放 deferred 对象。
复制代码
// 在executeCB、completeCB之间传递数据
struct AddonData {
napi_async_work asyncWork = nullptr;
napi_deferred deferred = nullptr;
napi_ref callback = nullptr;
  1. double args[2] = {0};
  2. double result = 0;
复制代码
};
// 2、执行耗时使命,并将执行结果传递给 promise;
static void addExecuteCB(napi_env env, void *data) {
AddonData *addonData = (AddonData *)data;
addonData->result = addonData->args[0] + addonData->args[1];
};
// 3、使用 napi_resolve_deferred 或 napi_reject_deffered 接口来 resolve 或 reject 创建的 promise,并开释 deferred 对象;
static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
AddonData *addonData = (AddonData *)data;
napi_value result = nullptr;
napi_create_double(env, addonData->result, &result);
napi_resolve_deferred(env, addonData->deferred, result);
  1. if (addonData->callback != nullptr) {
  2.     napi\_delete\_reference(env, addonData->callback);
  3. }
  4. // 删除异步 work
  5. napi\_delete\_async\_work(env, addonData->asyncWork);
  6. delete addonData;
  7. addonData = nullptr;
复制代码
};
// 1、用 napi_create_promise 接口创建 promise,将创建一个 deferred 对象并与 promise 一起返回,deferred
// 对象会绑定到已创建的 promise;
static napi_value addPromise(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2];
napi_value thisArg = nullptr;
napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr);
  1. napi_valuetype valuetype0;
  2. napi\_typeof(env, args[0], &valuetype0);
  3. napi_valuetype valuetype1;
  4. napi\_typeof(env, args[1], &valuetype1);
  5. if (valuetype0 != napi_number||valuetype1 != napi_number) {
  6.     napi\_throw\_type\_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
  7.     return NULL;
  8. }
  9. napi_value promise = nullptr;
  10. napi_deferred deferred = nullptr;
  11. napi\_create\_promise(env, &deferred, &promise);
  12. // 异步工作项上下文用户数据,传递到异步工作项的execute、complete之间传递数据
  13. auto addonData = new AddonData{
  14.     .asyncWork = nullptr,
  15.     .deferred = deferred,
  16. };
  17. napi\_get\_value\_double(env, args[0], &addonData->args[0]);
  18. napi\_get\_value\_double(env, args[1], &addonData->args[1]);
  19. // 创建async work,创建成功后通过最后一个参数(addonData->asyncWork)返回async work的handle
  20. napi_value resourceName = nullptr;
  21. napi\_create\_string\_utf8(env, "addAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
  22. napi\_create\_async\_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void \*)addonData,
  23.                        &addonData->asyncWork);
  24. // 将刚创建的async work加到队列,由底层去调度执行
  25. napi\_queue\_async\_work(env, addonData->asyncWork);
  26. return promise;
复制代码
}
  1. 在异步操作完成后,回调函数将被调用,并将结果传递给 Promise 对象。在 JavaScript 中,可以使用 Promise 对象的 then() 方法来处理异步操作的结果。
复制代码
import hilog from ‘@ohos.hilog’;
import testNapi from ‘libentry.so’
@Entry
@Component
struct TestAdd {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text(“hello world”)
.onClick(() => {
let num1 = 2;
let num2 = 3;
testNapi.addPromise(num1, num2).then((result) => {
hilog.info(0x0000, ‘testTag’, ‘%{public}d’, result);
})
})
}
.width(‘100%’)
.height(‘100%’)
}
}
  1. #### 指定异步任务调度优先级
  2. Function Flow 编程模型(Function Flow Runtime,FFRT)是一种基于任务和数据驱动的并发编程模型,允许开发者通过任务及其依赖关系描述的方式进行应用开发。方舟 ArkTS 运行时提供了扩展 qos 信息的接口,支持传入 qos,并调用 FFRT,根据系统资源使用情况降低功耗、提升性能。
  3. * 接口示例:napi\_status napi\_queue\_async\_work\_with\_qos(napi\_env env, napi\_async\_work work, napi\_qos\_t qos)()
  4.         + [in] env:调用API的环境;
  5.         + [in] napi\_async\_work: 异步任务;
  6.         + [in] napi\_qos\_t: qos 等级;
  7. * qos 等级定义:
复制代码
  1. typedef enum {
  2. napi_qos_background = 0,
  3. napi_qos_utility = 1,
  4. napi_qos_default = 2,
  5. napi_qos_user_initiated = 3,
  6. } napi_qos_t;
复制代码
  1. * N-API 层封装了对外的接口,对接 libuv 层 uv\_queue\_work\_with\_qos(uv\_loop\_t\* loop, uv\_work\_t\* req, uv\_work\_cb work\_cb, uv\_after\_work\_cb after\_work\_cb, uv\_qos\_t qos) 函数。
  2. * 相较于已有接口 napi\_queue\_async\_work,增加了 qos 等级,用于控制任务调度的优先级。使用示例: “`cpp static void PromiseOnExec(napi\_env env, void \*data) { OH\_LOG\_INFO(LOG\_APP, “PromiseOnExec”); }
复制代码
static void PromiseOnComplete(napi_env env, napi_status status, void *data) {
int number = *((int *)data); OH_LOG_INFO(LOG_APP, “PromiseOnComplete number = %{public}d”, number);
}
static napi_value Test(napi_env env, napi_callback_info info) {
napi_value resourceName = nullptr;
napi_create_string_utf8(env, “TestExample”, NAPI_AUTO_LENGTH, &resourceName);
napi_async_work async_work; int *data = new int(10); napi_create_async_work(env, nullptr, resourceName, PromiseOnExec, PromiseOnComplete, data, &async_work);
napi_queue_async_work_with_qos(env, async_work, napi_qos_default); return nullptr;
}
  1. ### 线程安全
  2. 如果应用需要进行大量的计算或者 IO 操作,使用并发机制可以充分利用多核 CPU 的优势,提高应用的处理效率。例如,图像处理、视频编码、数据分析等应用可以使用并发机制来提高处理速度。
  3. 虽然 N-API 本身不支持多线程并发操作,但是可以在多线程环境下进行一些数据交互,且需要格外注意线程安全。在多线程环境下,开发者可以使用 napi\_create\_threadsafe\_function 函数创建一个线程安全函数,然后在任意线程中调用。
  4. \*\*应用场景:\*\*当 native 侧有其他线程,并且需要根据这些线程的完成结果调用 JavaScript 函数时,这些线程必须与 native 侧的主线程进行通信,才能在主线程中调用 JavaScript 函数。线程安全函数便提供了一种简化方法,避免了线程间通讯,同时可以回到主线程调用 JavaScript 函数。
  5. #### 使用方法
  6. ##### ArkTS 侧传入回调函数
复制代码
struct Index {
@State message: string = ‘Hello World’
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
testNapi.threadSafeTest((value) => {
hilog.info(0x0000, ‘testTag’, 'js callback value = ’ + value);
})
})
}
.width(‘100%’)
}
.height(‘100%’)
}
}
  1. ##### native 侧主线程中创建线程安全函数
复制代码
static void CallJs(napi_env env, napi_value js_cb, void *context, void *data) {
  1. std::thread::id this_id = std::this_thread::get\_id();
  2. OH\_LOG\_INFO(LOG_APP, "thread CallJs %{public}d.\n", this_id);
  3. napi_status status;
  4. status = napi\_get\_reference\_value(env, cbObj, &js_cb);
  5. napi_valuetype valueType = napi_undefined;
  6. napi\_typeof(env, js_cb, &valueType);
  7. OH\_LOG\_INFO(LOG_APP, "CallJs js\_cb is napi\_function: %{public}d", valueType == napi_function);
  8. OH\_LOG\_INFO(LOG_APP, "CallJs 0");
  9. if (env != NULL) {
  10.     napi_value undefined, js_the_prime;
  11.     status = napi\_create\_int32(env, 666, &js_the_prime);
  12.     OH\_LOG\_INFO(LOG_APP, "CallJs 1: %{public}d", status == napi_ok);
  13.     status = napi\_get\_undefined(env, &undefined);
  14.     OH\_LOG\_INFO(LOG_APP, "CallJs 2: %{public}d", status == napi_ok);
  15.     napi_value ret;
  16.     status = napi\_call\_function(env, undefined, js_cb, 1, &js_the_prime, &ret);
  17.     OH\_LOG\_INFO(LOG_APP, "CallJs 3: %{public}d", status == napi_ok);
  18. }
复制代码
}
napi_threadsafe_function tsfn;
static napi_value ThreadSafeTest(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value js_cb, work_name;
napi_status status;
  1. status = napi\_get\_cb\_info(env, info, &argc, &js_cb, NULL, NULL);
  2. OH\_LOG\_INFO(LOG_APP, "ThreadSafeTest 0: %{public}d", status == napi_ok);
  3. status = napi\_create\_reference(env, js_cb, 1, &cbObj);
  4. OH\_LOG\_INFO(LOG_APP, "napi\_create\_reference of js\_cb to cbObj: %{public}d", status == napi_ok);
  5. status =
  6.     napi\_create\_string\_utf8(env, "Node-API Thread-safe Call from Async Work Item", NAPI_AUTO_LENGTH, &work_name);
  7. OH\_LOG\_INFO(LOG_APP, "ThreadSafeTest 1: %{public}d", status == napi_ok);
  8. std::thread::id this_id = std::this_thread::get\_id();
  9. OH\_LOG\_INFO(LOG_APP, "thread ThreadSafeTest %{public}d.\n", this_id);
  10. napi_valuetype valueType = napi_undefined;
  11. napi\_typeof(env, js_cb, &valueType);
  12. OH\_LOG\_INFO(LOG_APP, "ThreadSafeTest js\_cb is napi\_function: %{public}d", valueType == napi_function);
  13. status = napi\_create\_threadsafe\_function(env, js_cb, NULL, work_name, 0, 1, NULL, NULL, NULL, CallJs, &tsfn);
  14. OH\_LOG\_INFO(LOG_APP, "ThreadSafeTest 2: %{public}d", status == napi_ok);
复制代码
}
  1. ##### 其他线程中调用线程安全函数
复制代码
std::thread t( {
std::thread::id this_id = std::this_thread::get_id();
OH_LOG_INFO(LOG_APP, “thread0 %{public}d.\n”, this_id);
napi_status status;
status = napi_acquire_threadsafe_function(tsfn);
OH_LOG_INFO(LOG_APP, “thread1 : %{public}d”, status == napi_ok);
status = napi_call_threadsafe_function(tsfn, NULL, napi_tsfn_blocking);
OH_LOG_INFO(LOG_APP, “thread2 : %{public}d”, status == napi_ok);
});
t.detach();
  1. #### 线程函数使用注意事项
  2. 在多线程环境下,需要避免使用共享的数据结构和全局变量,以免竞争和冲突。同时,需要确保线程之间的同步和互斥,以避免数据不一致的情况发生。除此之外,仍需注意:
  3. * 对线程安全函数的调用是异步进行的,对 JavaScript 回调的调用将被放置在任务队列中;
  4. * 创建 napi\_threadsafe\_function 时,可以提供 napi\_finalize 回调。当线程安全函数即将被销毁时,将在主线程上调用此 napi\_finalize 回调;
  5. * 在调用 napi\_create\_threadsafe\_function 时给定了上下文,可以从任何调用 napi\_get\_threadafe\_function\_context 的线程中获取。
  6. **为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:[`https://qr21.cn/FV7h05`]( )**
  7. #### 《鸿蒙 (OpenHarmony)开发学习手册》:[`https://qr21.cn/FV7h05`]( )
  8. **入门必看:[`https://qr21.cn/FV7h05`]( )**
  9. 1. 应用开发导读(ArkTS)
  10. 2. ……
  11. ![](https://img-blog.csdnimg.cn/img_convert/4991b6612f7848b8226a785e9bf2fbe9.webp?x-oss-process=image/format,png)
  12. **HarmonyOS 概念:[`https://qr21.cn/FV7h05`]( )**
  13. 1. 系统定义
  14. 2. 技术架构
  15. 3. 技术特性
  16. 4. 系统安全
  17. ![](https://img-blog.csdnimg.cn/img_convert/53c5d7c674e3b2bf227cbf917fccef69.webp?x-oss-process=image/format,png)
  18. **如何快速入门:[`https://qr21.cn/FV7h05`]( )**
  19. 1. 基本概念
  20. 2. 构建第一个ArkTS应用
  21. 3. 构建第一个JS应用
  22. 4. ……
  23. ![](https://img-blog.csdnimg.cn/img_convert/52d468782ba786e32203d4ea7c7afc2b.webp?x-oss-process=image/format,png)
  24. **开发基础知识:[`https://qr21.cn/FV7h05`]( )**
  25. 1. 应用基础知识
  26. 2. 配置文件
  27. 3. 应用数据管理
  28. 4. 应用安全管理
  29. 5. 应用隐私保护
  30. 6. 三方应用调用管控机制
  31. 7. 资源分类与访问
  32. 8. 学习ArkTS语言
  33. 9. ……
  34. ![](https://img-blog.csdnimg.cn/img_convert/2e9f9af56c68d6b543f76207bccf7bab.webp?x-oss-process=image/format,png)
  35. **基于ArkTS 开发:[`https://qr21.cn/FV7h05`]( )**
  36. 1. Ability开发
  37. 2. UI开发
  38. 3. 公共事件与通知
  39. 4. 窗口管理
  40. 5. 媒体
  41. 6. 安全
  42. 7. 网络与链接
  43. 8. 电话服务
  44. 9. 数据管理
  45. 10. 后台任务(Background Task)管理
  46. 11. 设备管理
  47. 12. 设备使用信息统计
  48. 13. DFX
  49. 14. 国际化开发
  50. ![img](https://img-blog.csdnimg.cn/img_convert/f6c98897713c53af8e379cbe2d16ad55.png)
  51. ![img](https://img-blog.csdnimg.cn/img_convert/afcfd598c653cba63bb6f27389148965.png)
  52. **网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
  53. **[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**
  54. **一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  55. UI开发
  56. 3. 公共事件与通知
  57. 4. 窗口管理
  58. 5. 媒体
  59. 6. 安全
  60. 7. 网络与链接
  61. 8. 电话服务
  62. 9. 数据管理
  63. 10. 后台任务(Background Task)管理
  64. 11. 设备管理
  65. 12. 设备使用信息统计
  66. 13. DFX
  67. 14. 国际化开发
  68. [外链图片转存中...(img-JNExQWx3-1715810881049)]
  69. [外链图片转存中...(img-6tOnZ1dk-1715810881049)]
  70. **网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
  71. **[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**
  72. **一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表