scope管理N-API对象

前言

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

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

解决思路

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

JS 侧
Row() {
  Text("handleScope")
    .fontSize(50)
    .fontWeight(FontWeight.Bold)
    .onClick(() => {
      hilog.info(0x0000, 
                 'testTag', 
                 'Test NAPI handleScope %{public}d',
                 // 调用 C++ 侧方法,传入数组
								 testNapi.handleScope([1,2,3,4,5,6])
                );
    })
}
C++侧
static napi_value handleScope(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value object;
    // 接收 JS 侧传入的数组,赋值给 object。
    napi_get_cb_info(env, info, &argc, &object, nullptr, nullptr);
    
    napi_handle_scope scope;
    // 创建一个新的作用域,赋值给 scope,这里是作用域的起点。
    napi_open_handle_scope(env, &scope);

    // 在作用域里定义 NAPI 对象
    napi_value eleVal;
    // 用数组的元素给对象赋值
    napi_get_element(env, object, 2, &eleVal);

    // 关闭 scope 作用域,此处为作用域终点
    napi_close_handle_scope(env, scope);

    // 作用域外,是访问不了作用域内的数据,比如 eleVal
    int32_t res;
    napi_get_value_int32(env, eleVal, &res);
    // eleVal 打印结果是 0,而不是 2,说明 eleVal 在函数结束前被销毁。
    // 去除作用域,打印结果是 2
    return eleVal;
}

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

解决思路

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

JS 侧
Row() {
  Text("handleEscapableScope")
    .fontSize(50)
    .fontWeight(FontWeight.Bold)
    .onClick(() => {
      hilog.info(0x0000, 
                 'testTag',
                 'Test NAPI handleEscapableScope %{public}d', 
                 // 调用 C++ 侧方法,传入数组
                 testNapi.handleEscapableScope([1,2,3,4,5,6])
                );
    })
}
C++侧
static napi_value handleEscapableScope(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value object;
    // 接收 JS 侧传入的数组,赋值给 object。
    napi_get_cb_info(env, info, &argc, &object, nullptr, nullptr);

    napi_value outerEleVal;
     
    napi_escapable_handle_scope scope;
    // 创建一个新的可逃逸作用域,赋值给 scope,这里是作用域的起点。
    napi_open_escapable_handle_scope(env, &scope);
    // 在作用域里定义将要逃逸的 NAPI 对象
    napi_value eleVal;
    // 用数组的元素给对象赋值
    napi_get_element(env, object, 2, &eleVal);
    // 将 eleVal 逃逸出作用域,外部用 outerEleVal 接收
    napi_escape_handle(env, scope, eleVal, &outerEleVal);
    // 关闭 scope 作用域,此处为作用域终点
    napi_close_escapable_handle_scope(env, scope);
    
    int32_t res;
    napi_get_value_int32(env, outerEleVal, &res);
    // outerEleVal 打印的结果是 2,说明指定的 NAPI 对象 eleVal,已逃逸出作用域
    return outerEleVal;
}

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

解决思路

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

JS 侧
Row() {
	Text("handleReference")
		.fontSize(50)
		.fontWeight(FontWeight.Bold)
		.onClick(() => {
			hilog.info(0x0000, 
					   'testTag', 
					   'Test NAPI handleReference %{public}d', 
					   // 调用 C++ 侧方法
					   testNapi.handleReference()
			);
		})
}
C++ 侧
// 定义引用
napi_ref ref;

// 使用异步 work,是为了模拟主函数结束后,在异步函数里去访问对象
// 验证主函数的 NAPI 对象是否随主函数结束而被销毁
struct AsyncWorkInfo {
    napi_async_work work;
};

struct AsyncWorkInfo data = { nullptr };

static void AExecute(napi_env env, void *data) {
	// 异步函数内
    struct AsyncWorkInfo *arg = (struct AsyncWorkInfo *)data;
    napi_value recv;
    // 获取引用值,用 recv 接收
    napi_get_reference_value(env, ref, &recv);
    
    int32_t res;
    napi_get_value_int32(env, recv, &res);
	// 打印引用值为 333,说明该引用映射的主函数 NAPI 对象没有随着主函数结束被销毁
	// 从而控制了 NAPI 对象的生命周期
    OH_LOG_INFO(LOG_APP, "ref val is %{pubilc}d", res);
}


static napi_value handleReference(napi_env env, napi_callback_info info)
{
	// 主函数内
    napi_value resourceName;
    napi_create_string_utf8(env, "asyncWork", NAPI_AUTO_LENGTH, &resourceName);

	// 定义主函数的 NAPI 对象
    napi_value test_data;
    napi_create_int32(env, 333, &test_data);

	// 给主函数 NAPI 创建绑定引用
    napi_create_reference(env, test_data, 1, &ref);
    
    struct AsyncWorkInfo *ptr = &data;
	// 异步执行 AExecute
    napi_create_async_work(env, nullptr, resourceName, AExecute, nullptr, ptr, &ptr->work);
    napi_queue_async_work(env, ptr->work);
    return resourceName;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值