鸿蒙5.0开发进阶:NDK代码开发-Node-API使用指南(使用Node-API接口举行生 ...

打印 上一主题 下一主题

主题 1491|帖子 1491|积分 4483

往期鸿蒙5.0全套实战文章必看:(文中附带全栈鸿蒙5.0学习资料)



  • 鸿蒙开发核心知识点,看这篇文章就够了
  • 最新版!鸿蒙HarmonyOS Next应用开发实战学习蹊径
  • 鸿蒙HarmonyOS NEXT开发技术最全学习蹊径指南
  • 鸿蒙应用开发实战项目,看这一篇文章就够了(部分项目附源码)

使用Node-API接口举行生命周期相关开发

简介

在Node-API中,napi_value是一个表示ArkTS值的抽象类型,它可以表示任何ArkTS值,包括基本类型(如数字、字符串、布尔值)和复杂对象类型(如数组、函数、对象等)。
napi_value的生命周期与其在ArkTS中的对应值的生命周期紧密相关。当ArkTS值被垃圾回收时,与之关联的napi_value也将不再有用。重要的是不要在ArkTS值不再存在时尝试使用napi_value。
框架层的scope通常用于管理napi_value的生命周期。在Node-API中,可以使用napi_open_handle_scope和napi_close_handle_scope函数来创建和销毁scope。通过在scope内创建napi_value,可以确保在scope竣事时主动释放napi_value,避免内存泄漏。
napi_ref是一个Node-API类型,用于管理napi_value的生命周期。napi_ref允许您在napi_value的生命周期内保持对其的引用,纵然它已经超出了其原始上下文的范围。这使得您可以在不同的上下文中共享napi_value,并确保在不再需要时正确释放其内存。
基本概念

Node-API提供了一组功能,使开发职员能够在Node-API模块中创建和操作ArkTS对象,管理引用和生命周期,并注册垃圾回收回调函数等。下面是一些基本概念:


  • 作用域:用于创建一个范围,在范围内声明的引用在范围外部将不再见效。Node-API提供了创建、关闭普通和可逃逸的作用域的函数。
  • 引用管理:Node-API提供函数来创建、删除和管理对象的引用,以延长对象的生命周期,避免出现对象use-after-free的问题。同时也通过引用管理去避免发生内存泄漏的问题。
  • 可逃逸的作用域:允许在创建的作用域中声明的对象返回到父作用域,通过napi_open_escapable_handle_scope和napi_close_escapable_handle_scope举行管理。
  • 垃圾回收回调:允许注册回调函数,以便在ArkTS对象被垃圾回收时实行特定的清理操作。
这些基本概念使开发职员能够在Node-API模块中安全且有用地操作ArkTS对象,并确保正确管理对象的生命周期。
场景和功能先容

以下Node-API接口主要用于ArkTS对象的引用管理,并确保在Node-API模块代码中正确地处置惩罚ArkTS对象的生命周期。使用场景如下:
接口形貌napi_open_handle_scope、napi_close_handle_scope主要用于管理ArkTS对象的生命周期,确保在Node-API模块代码中使用ArkTS对象时能够正确地举行内存管理。当在Node-API模块中处置惩罚ArkTS对象时,需要创建一个暂时的作用域来存储对象的引用,以便在实行期间正确访问这些对象,并在实行竣过后关闭这个handle scope。napi_open_escapable_handle_scope、napi_close_escapable_handle_scope当在Node-API模块中编写函数实现,需要将函数在ArkTS中返回的对象从函数的作用域正确地返回到函数被调用的外部作用域中。napi_escape_handle需要将ArkTS对象的生命周期提拔到父作用域中,避免对象被意外释放。napi_create_reference、napi_delete_reference主要用于在Node-API模块代码中管理ArkTS对象的引用,以确保对象的生命周期符合插件的需求。napi_reference_ref、napi_reference_unref主要用于管理ArkTS对象引用的引用计数,以确保在多个地方共享引用时引用计数能够正确地增长和减少。napi_get_reference_value主要用于在Node-API模块代码中获取与引用相关联的ArkTS对象,以便在Node-API模块中对其举行操作。napi_add_finalizer在需要在ArkTS对象被垃圾回收前实行一些清理或释放资源的情况下,确保资源的正确释放和管理。 使用示例

Node-API接口开发流程参考使用Node-API实现跨语言交互开发流程,本文仅对接口对应C++及ArkTS相关代码举行展示。
napi_open_handle_scope、napi_close_handle_scope

通过接口napi_open_handle_scope创建一个上下文情况使用。需要使用napi_close_handle_scope举行关闭。用于管理ArkTS对象的生命周期确保在Node-API模块代码处置惩罚ArkTS对象时能够正确地管理其句柄,以避免出现对象错误回收的问题。
需要注意的是公道使用napi_open_handle_scope和napi_close_handle_scope管理napi_value的生命周期,做到生命周期最小化,避免发生内存泄漏问题。
代码部分也可参考下面链接:
生命周期管理
cpp部分代码
  1. #include "napi/native_api.h"
  2. static napi_value HandleScopeTest(napi_env env, napi_callback_info info)
  3. {
  4.     // 通过调用napi_open_handle_scope来创建一个句柄作用域
  5.     napi_handle_scope scope;
  6.     napi_open_handle_scope(env, &scope);
  7.     // 在句柄作用域内创建一个obj
  8.     napi_value obj = nullptr;
  9.     napi_create_object(env, &obj);
  10.     // 在对象中添加属性
  11.     napi_value value = nullptr;
  12.     napi_create_string_utf8(env, "handleScope", NAPI_AUTO_LENGTH, &value);
  13.     napi_set_named_property(env, obj, "key", value);
  14.     // 在作用域内获取obj的属性并返回
  15.     napi_value result = nullptr;
  16.     napi_get_named_property(env, obj, "key", &result);
  17.     // 关闭句柄作用域,自动释放在该作用域内创建的对象句柄
  18.     napi_close_handle_scope(env, scope);
  19.     // 此处的result能够得到值“handleScope”
  20.     return result;
  21. }
  22. static napi_value HandleScope(napi_env env, napi_callback_info info)
  23. {
  24.     // 通过调用napi_open_handle_scope来创建一个句柄作用域
  25.     napi_handle_scope scope;
  26.     napi_open_handle_scope(env, &scope);
  27.     // 在句柄作用域内创建一个obj
  28.     napi_value obj = nullptr;
  29.     napi_create_object(env, &obj);
  30.     // 在对象中添加属性
  31.     napi_value value = nullptr;
  32.     napi_create_string_utf8(env, "handleScope", NAPI_AUTO_LENGTH, &value);
  33.     napi_set_named_property(env, obj, "key", value);
  34.     // 关闭句柄作用域,自动释放在该作用域内创建的对象句柄
  35.     napi_close_handle_scope(env, scope);
  36.     // 在作用域外获取obj的属性并返回,此处只能得到“undefined”
  37.     napi_value result = nullptr;
  38.     napi_get_named_property(env, obj, "key", &result);
  39.     return result;
  40. }
复制代码
接口声明
  1. // index.d.ts
  2. export const handleScopeTest: () => string;
  3. export const handleScope: () => string;
复制代码
ArkTS侧示例代码
  1. import hilog from '@ohos.hilog'
  2. import testNapi from 'libentry.so'
  3. try {
  4.   hilog.info(0x0000, 'testTag', 'Test Node-API handleScopeTest: %{public}s', testNapi.handleScopeTest());
  5.   hilog.info(0x0000, 'testTag', 'Test Node-API handleScope: %{public}s', testNapi.handleScope());
  6. } catch (error) {
  7.   hilog.error(0x0000, 'testTag', 'Test Node-API handleScopeTest errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
  8. }
复制代码
napi_open_escapable_handle_scope、napi_close_escapable_handle_scope、napi_escape_handle

通过接口napi_open_escapable_handle_scope创建出一个可逃逸的handel scope,可将范围内声明的值返回到父作用域。需要使用napi_close_escapable_handle_scope举行关闭。napi_escape_handle用于提拔传入的ArkTS对象的生命周期到其父作用域。
通过上述接口可以更灵活的使用管理传入的ArkTS对象,特别是在处置惩罚跨作用域的值通报时非常有用。
cpp部分代码
  1. #include "napi/native_api.h"
  2. static napi_value EscapableHandleScopeTest(napi_env env, napi_callback_info info)
  3. {
  4.     // 创建一个可逃逸的句柄作用域
  5.     napi_escapable_handle_scope scope;
  6.     napi_open_escapable_handle_scope(env, &scope);
  7.     // 在可逃逸的句柄作用域内创建一个obj
  8.     napi_value obj = nullptr;
  9.     napi_create_object(env, &obj);
  10.     // 在对象中添加属性
  11.     napi_value value = nullptr;
  12.     napi_create_string_utf8(env, "Test napi_escapable_handle_scope", NAPI_AUTO_LENGTH, &value);
  13.     napi_set_named_property(env, obj, "key", value);
  14.     // 调用napi_escape_handle将对象逃逸到作用域之外
  15.     napi_value escapedObj = nullptr;
  16.     napi_escape_handle(env, scope, obj, &escapedObj);
  17.     // 关闭可逃逸的句柄作用域,清理资源
  18.     napi_close_escapable_handle_scope(env, scope);
  19.     // 在获取逃逸后的obj:escapedObj的属性并返回,此处也能够得到“napi_escapable_handle_scope”
  20.     napi_value result = nullptr;
  21.     // 为了验证逃逸的实现,可以在此处获取obj的属性,此处会得到“undefined”
  22.     napi_get_named_property(env, escapedObj, "key", &result);
  23.     return result;
  24. }
复制代码
接口声明
  1. // index.d.ts
  2. export const escapableHandleScopeTest: () => string;
复制代码
ArkTS侧示例代码
  1. import hilog from '@ohos.hilog'
  2. import testNapi from 'libentry.so'
  3. try {
  4.   hilog.info(0x0000, 'testTag', 'Test Node-API EscapableHandleScopeTest: %{public}s', testNapi.escapableHandleScopeTest());
  5. } catch (error) {
  6.   hilog.error(0x0000, 'testTag', 'Test Node-API EscapableHandleScopeTest errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
  7. }
复制代码
napi_create_reference、napi_delete_reference

为Object创建一个reference,以延长其生命周期。调用者需要自己管理reference生命周期。可以调用napi_delete_reference删除传入的reference。
napi_reference_ref、napi_reference_unref

增长/减少传入的reference的引用计数,并获取新的计数。
napi_get_reference_value

获取与reference相关联的ArkTS Object。
   阐明
  由于弱引用(引用计数为0的napi_ref)的释放与gc回收js对象并非同时发生。
  因此可能在弱引用被释放前,js对象已经被回收。
  这意味着你可能在napi_ref有用的情况下,通过本接口获取到一个空指针。
  napi_add_finalizer

当ArkTS Object中的对象被垃圾回收时调用注册的napi_add_finalizer回调。
cpp部分代码
  1. // log.h用于C++中日志打印
  2. #include "hilog/log.h"
  3. #include "napi/native_api.h"
  4. // 创建一个指向napi_ref类型的指针,用于存储创建的引用。在调用napi_create_reference函数之前,你需要分配一个napi_ref类型的变量,并将其地址传递给result位置的参数
  5. napi_ref g_ref;
  6. void Finalizer(napi_env env, void *data, void *hint)
  7. {
  8.     // 执行资源清理操作
  9.     OH_LOG_INFO(LOG_APP, "Node-API: Use terminators to release resources.");
  10. }
  11. static napi_value CreateReference(napi_env env, napi_callback_info info)
  12. {
  13.     napi_value obj = nullptr;
  14.     napi_create_object(env, &obj);
  15.     napi_value value = nullptr;
  16.     napi_create_string_utf8(env, "CreateReference", NAPI_AUTO_LENGTH, &value);
  17.     // 将键值对添加到对象中
  18.     napi_set_named_property(env, obj, "key", value);
  19.     // 创建对ArkTS对象的引用
  20.     napi_status status = napi_create_reference(env, obj, 1, &g_ref);
  21.     if (status != napi_ok) {
  22.         napi_throw_error(env, nullptr, "napi_create_reference fail");
  23.         return nullptr;
  24.     }
  25.     // 添加终结器
  26.     void *data = {};
  27.     napi_add_finalizer(env, obj, data, Finalizer, nullptr, &g_ref);
  28.     // 增加传入引用的引用计数并返回生成的引用计数
  29.     uint32_t result = 0;
  30.     napi_reference_ref(env, g_ref, &result);
  31.     OH_LOG_INFO(LOG_APP, "napi_reference_ref, count = %{public}d.", result);
  32.     if (result != 2) {
  33.         // 若传入引用的引用计数未增加,则抛出错误
  34.         napi_throw_error(env, nullptr, "napi_reference_ref fail");
  35.         return nullptr;
  36.     }
  37.     return obj;
  38. }
  39. static napi_value UseReference(napi_env env, napi_callback_info info)
  40. {
  41.     napi_value obj = nullptr;
  42.     // 通过调用napi_get_reference_value获取引用的ArkTS对象
  43.     napi_status status = napi_get_reference_value(env, g_ref, &obj);
  44.     if (status != napi_ok) {
  45.         napi_throw_error(env, nullptr, "napi_get_reference_value fail");
  46.         return nullptr;
  47.     }
  48.     // 将获取到的对象返回
  49.     return obj;
  50. }
  51. static napi_value DeleteReference(napi_env env, napi_callback_info info)
  52. {
  53.     // 减少传入引用的引用计数并返回生成的引用计数
  54.     uint32_t result = 0;
  55.     napi_value count = nullptr;
  56.     napi_reference_unref(env, g_ref, &result);
  57.     OH_LOG_INFO(LOG_APP, "napi_reference_ref, count = %{public}d.", result);
  58.     if (result != 1) {
  59.         // 若传入引用的引用计数未减少,则抛出错误
  60.         napi_throw_error(env, nullptr, "napi_reference_unref fail");
  61.         return nullptr;
  62.     }
  63.     // 通过调用napi_delete_reference删除对ArkTS对象的引用
  64.     napi_status status = napi_delete_reference(env, g_ref);
  65.     if (status != napi_ok) {
  66.         napi_throw_error(env, nullptr, "napi_delete_reference fail");
  67.         return nullptr;
  68.     }
  69.     napi_value returnResult = nullptr;
  70.     napi_create_string_utf8(env, "napi_delete_reference success", NAPI_AUTO_LENGTH, &returnResult);
  71.     return returnResult;
  72. }
复制代码
接口声明
  1. // index.d.ts
  2. export const createReference: () => Object | void;
  3. export const useReference: () => Object | void;
  4. export const deleteReference: () => string | void;
复制代码
ArkTS侧示例代码
  1. import hilog from '@ohos.hilog'
  2. import testNapi from 'libentry.so'
  3. try {
  4.   hilog.info(0x0000, 'testTag', 'Test Node-API createReference: %{public}s', JSON.stringify(testNapi.createReference()));
  5.   hilog.info(0x0000, 'testTag', 'Test Node-API useReference: %{public}s', JSON.stringify(testNapi.useReference()));
  6.   hilog.info(0x0000, 'testTag', 'Test Node-API deleteReference: %{public}s', testNapi.deleteReference());
  7. } catch (error) {
  8.   hilog.error(0x0000, 'testTag', 'Test Node-API ReferenceTest errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
  9. }
复制代码
以上代码如果要在native cpp中打印日记,需在CMakeLists.txt文件中添加以下设置信息(并添加头文件:#include "hilog/log.h"):
  1. // CMakeLists.txt
  2. add_definitions( "-DLOG_DOMAIN=0xd0d0" )
  3. add_definitions( "-DLOG_TAG="testTag"" )
  4. target_link_libraries(entry PUBLIC libhilog_ndk.z.so)
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

来自云龙湖轮廓分明的月亮

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