当前位置:   article > 正文

scope管理N-API对象_napi::escapablehandlescope

napi::escapablehandlescope

前言

N-API 调用时底层 VM 的堆中对象会返回 napi_value 句柄。函数代码运行时,这些句柄对象一直保持活跃;待函数结束时,才会被 GC 回收。 N-API 对象的生命周期管理本质上是控制对象什么时机被销毁。主要通过添加作用域和 create reference 方式。

场景一:执行的函数还没结束,出现大量 N-API Value 闲置句柄,消耗资源,影响函数后部的执行性能

解决思路

在函数执行结束前,提前销毁这个 NAPI 对象;使用 napi_open/close__handle_scope 在函数创建作用域,从该作用域跳出后,里面的变量被销毁。

JS 侧
  1. Row() {
  2. Text("handleScope")
  3. .fontSize(50)
  4. .fontWeight(FontWeight.Bold)
  5. .onClick(() => {
  6. hilog.info(0x0000,
  7. 'testTag',
  8. 'Test NAPI handleScope %{public}d',
  9. // 调用 C++ 侧方法,传入数组
  10. testNapi.handleScope([1,2,3,4,5,6])
  11. );
  12. })
  13. }
C++侧
  1. static napi_value handleScope(napi_env env, napi_callback_info info)
  2. {
  3. size_t argc = 1;
  4. napi_value object;
  5. // 接收 JS 侧传入的数组,赋值给 object。
  6. napi_get_cb_info(env, info, &argc, &object, nullptr, nullptr);
  7. napi_handle_scope scope;
  8. // 创建一个新的作用域,赋值给 scope,这里是作用域的起点。
  9. napi_open_handle_scope(env, &scope);
  10. // 在作用域里定义 NAPI 对象
  11. napi_value eleVal;
  12. // 用数组的元素给对象赋值
  13. napi_get_element(env, object, 2, &eleVal);
  14. // 关闭 scope 作用域,此处为作用域终点
  15. napi_close_handle_scope(env, scope);
  16. // 作用域外,是访问不了作用域内的数据,比如 eleVal
  17. int32_t res;
  18. napi_get_value_int32(env, eleVal, &res);
  19. // eleVal 打印结果是 0,而不是 2,说明 eleVal 在函数结束前被销毁。
  20. // 去除作用域,打印结果是 2
  21. return eleVal;
  22. }

场景二:在场景一的基础上,需要作用域内指定变量能够在作用域外访问

解决思路

使用 napi_open/close_escapable_handle_scope,如其名 escapable 可逃逸的,这种作用域里的变量是可逃逸到外部作用域,但是需要 napi_escape_handle 指定哪个对象。

JS 侧
  1. Row() {
  2. Text("handleEscapableScope")
  3. .fontSize(50)
  4. .fontWeight(FontWeight.Bold)
  5. .onClick(() => {
  6. hilog.info(0x0000,
  7. 'testTag',
  8. 'Test NAPI handleEscapableScope %{public}d',
  9. // 调用 C++ 侧方法,传入数组
  10. testNapi.handleEscapableScope([1,2,3,4,5,6])
  11. );
  12. })
  13. }
C++侧
  1. static napi_value handleEscapableScope(napi_env env, napi_callback_info info)
  2. {
  3. size_t argc = 1;
  4. napi_value object;
  5. // 接收 JS 侧传入的数组,赋值给 object。
  6. napi_get_cb_info(env, info, &argc, &object, nullptr, nullptr);
  7. napi_value outerEleVal;
  8. napi_escapable_handle_scope scope;
  9. // 创建一个新的可逃逸作用域,赋值给 scope,这里是作用域的起点。
  10. napi_open_escapable_handle_scope(env, &scope);
  11. // 在作用域里定义将要逃逸的 NAPI 对象
  12. napi_value eleVal;
  13. // 用数组的元素给对象赋值
  14. napi_get_element(env, object, 2, &eleVal);
  15. // 将 eleVal 逃逸出作用域,外部用 outerEleVal 接收
  16. napi_escape_handle(env, scope, eleVal, &outerEleVal);
  17. // 关闭 scope 作用域,此处为作用域终点
  18. napi_close_escapable_handle_scope(env, scope);
  19. int32_t res;
  20. napi_get_value_int32(env, outerEleVal, &res);
  21. // outerEleVal 打印的结果是 2,说明指定的 NAPI 对象 eleVal,已逃逸出作用域
  22. return outerEleVal;
  23. }

场景三:需要函数里 NAPI 对象在函数执行结束后,仍然不被销毁

解决思路

引用计数垃圾收集策略 给函数里的 NAPI 对象创建 reference,通过控制 reference 的引用计数不为 0,让该对象不被回收销毁

JS 侧
  1. Row() {
  2. Text("handleReference")
  3. .fontSize(50)
  4. .fontWeight(FontWeight.Bold)
  5. .onClick(() => {
  6. hilog.info(0x0000,
  7. 'testTag',
  8. 'Test NAPI handleReference %{public}d',
  9. // 调用 C++ 侧方法
  10. testNapi.handleReference()
  11. );
  12. })
  13. }
C++ 侧
  1. // 定义引用
  2. napi_ref ref;
  3. // 使用异步 work,是为了模拟主函数结束后,在异步函数里去访问对象
  4. // 验证主函数的 NAPI 对象是否随主函数结束而被销毁
  5. struct AsyncWorkInfo {
  6. napi_async_work work;
  7. };
  8. struct AsyncWorkInfo data = { nullptr };
  9. static void AExecute(napi_env env, void *data) {
  10. // 异步函数内
  11. struct AsyncWorkInfo *arg = (struct AsyncWorkInfo *)data;
  12. napi_value recv;
  13. // 获取引用值,用 recv 接收
  14. napi_get_reference_value(env, ref, &recv);
  15. int32_t res;
  16. napi_get_value_int32(env, recv, &res);
  17. // 打印引用值为 333,说明该引用映射的主函数 NAPI 对象没有随着主函数结束被销毁
  18. // 从而控制了 NAPI 对象的生命周期
  19. OH_LOG_INFO(LOG_APP, "ref val is %{pubilc}d", res);
  20. }
  21. static napi_value handleReference(napi_env env, napi_callback_info info)
  22. {
  23. // 主函数内
  24. napi_value resourceName;
  25. napi_create_string_utf8(env, "asyncWork", NAPI_AUTO_LENGTH, &resourceName);
  26. // 定义主函数的 NAPI 对象
  27. napi_value test_data;
  28. napi_create_int32(env, 333, &test_data);
  29. // 给主函数 NAPI 创建绑定引用
  30. napi_create_reference(env, test_data, 1, &ref);
  31. struct AsyncWorkInfo *ptr = &data;
  32. // 异步执行 AExecute
  33. napi_create_async_work(env, nullptr, resourceName, AExecute, nullptr, ptr, &ptr->work);
  34. napi_queue_async_work(env, ptr->work);
  35. return resourceName;
  36. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/616386
推荐阅读
相关标签
  

闽ICP备14008679号