使用扩展的Node-API接口创建对ArkTS对象的Sendable强引用
HarmonyOS的API提供进程内跨ArkTS线程共享的强引用能力。相较于napi_ref,napi_sendable_ref支持跨ArkTS线程操作,但同时也存在一些限制。
场景介绍
开发者可使用napi_create_strong_sendable_reference接口创建指向Sendable ArkTS对象的Sendable强引用,使用napi_get_strong_sendable_reference_value获取被引用的ArkTS对象,使用napi_delete_strong_sendable_reference删除Sendable强引用。这些操作既可以在同一ArkTS线程进行,也可在不同ArkTS线程进行。
Sendable强引用对象关联接口
| 接口 | 描述 |
|---|---|
| napi_create_strong_sendable_reference | 创建指向Sendable ArkTS对象的Sendable强引用。 |
| napi_delete_strong_sendable_reference | 删除Sendable强引用。 |
| napi_get_strong_sendable_reference_value | 根据Sendable强引用获取其关联的ArkTS对象值。 |
示例代码
-
模块注册
// napi_init.cpp#include "napi/native_api.h"#include <cstdlib>#include <thread>#define ASSERT_EQ(a, b) \do { \if (a != b) { \std::abort(); \} \} while(0)#define ASSERT_CHECK_CALL(a) ASSERT_EQ(a, napi_ok)napi_sendable_ref sRef = nullptr;static napi_value CreateSendableRef(napi_env env, napi_callback_info info){size_t argc = 1;napi_value args[1] = {nullptr};ASSERT_CHECK_CALL(napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));ASSERT_CHECK_CALL(napi_create_strong_sendable_reference(env, args[0], &sRef));napi_value str = nullptr;ASSERT_CHECK_CALL(napi_create_string_utf8(env, "success", NAPI_AUTO_LENGTH, &str));return str;}static napi_value GetAndModifySendableRefValueInArkRuntime(napi_env env, napi_callback_info info){// 此处省略调用者在主线程的业务逻辑// ...// 此处模拟调用者在其他ArkTS线程上获取napi_sendable_ref内的共享对象操作std::thread t1([]() {napi_env newEnv = nullptr;ASSERT_CHECK_CALL(napi_create_ark_runtime(&newEnv));napi_handle_scope scope = nullptr;ASSERT_CHECK_CALL(napi_open_handle_scope(newEnv, &scope));if (!sRef) {std::abort();}napi_value sObj = nullptr;ASSERT_CHECK_CALL(napi_get_strong_sendable_reference_value(newEnv, sRef, &sObj));// 校验sObj内容napi_value numValue = nullptr;ASSERT_CHECK_CALL(napi_get_named_property(newEnv, sObj, "num", &numValue));int32_t num = 0;ASSERT_CHECK_CALL(napi_get_value_int32(newEnv, numValue, &num));ASSERT_EQ(num, 1111);// 修改sObj内容napi_value newNum = nullptr;ASSERT_CHECK_CALL(napi_create_int32(newEnv, num * 2, &newNum));ASSERT_CHECK_CALL(napi_set_named_property(newEnv, sObj, "num", newNum));ASSERT_CHECK_CALL(napi_close_handle_scope(newEnv, scope));ASSERT_CHECK_CALL(napi_destroy_ark_runtime(&newEnv));});t1.join();napi_value str = nullptr;ASSERT_CHECK_CALL(napi_create_string_utf8(env, "success", NAPI_AUTO_LENGTH, &str));return str;}static napi_value GetSendableRefValueInWorkerOrTaskpool(napi_env env, napi_callback_info info){// 此处省略调用者在worker/taskpool线程的业务逻辑// ...// 此处模拟调用者在其他Worker/Taskpool线程上获取napi_sendable_ref内的共享对象操作if (!sRef) {napi_value undefined = nullptr;napi_get_undefined(env, &undefined);return undefined;}napi_value sObj = nullptr;ASSERT_CHECK_CALL(napi_get_strong_sendable_reference_value(env, sRef, &sObj));// 校验sObj内容napi_value numValue = nullptr;ASSERT_CHECK_CALL(napi_get_named_property(env, sObj, "num", &numValue));int32_t num = 0;ASSERT_CHECK_CALL(napi_get_value_int32(env, numValue, &num));ASSERT_EQ(num, 1111);return sObj;}static napi_value CheckAndDeleteSendableRef(napi_env env, napi_callback_info info){if (!sRef) {napi_value undefined = nullptr;ASSERT_CHECK_CALL(napi_get_undefined(env, &undefined));return undefined;}// 校验和删除ref的动作也可放在ArkTS线程中,此处示例为主线程// 校验sObj内容napi_value sObj = nullptr;ASSERT_CHECK_CALL(napi_get_strong_sendable_reference_value(env, sRef, &sObj));napi_value numValue = nullptr;ASSERT_CHECK_CALL(napi_get_named_property(env, sObj, "num", &numValue));int32_t num = 0;ASSERT_CHECK_CALL(napi_get_value_int32(env, numValue, &num));ASSERT_EQ(num, 2222);ASSERT_CHECK_CALL(napi_delete_strong_sendable_reference(env, sRef));sRef = nullptr;// 删除SendableRefnapi_value str = nullptr;ASSERT_CHECK_CALL(napi_create_string_utf8(env, "success", NAPI_AUTO_LENGTH, &str));return str;}EXTERN_C_STARTstatic napi_value Init(napi_env env, napi_value exports){napi_property_descriptor desc[] = {{ "createSendableRef", nullptr, CreateSendableRef, nullptr, nullptr, nullptr, napi_default, nullptr },{ "getAndModifySendableRefValueInArkRuntime", nullptr, GetAndModifySendableRefValueInArkRuntime,nullptr,nullptr, nullptr, napi_default, nullptr },{ "getSendableRefValueInWorkerOrTaskpool", nullptr, GetSendableRefValueInWorkerOrTaskpool,nullptr,nullptr, nullptr, napi_default, nullptr },{ "checkAndDeleteSendableRef", nullptr, CheckAndDeleteSendableRef,nullptr, nullptr, nullptr, napi_default, nullptr },};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;}EXTERN_C_ENDstatic napi_module demoModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init,.nm_modname = "entry",.nm_priv = ((void*)0),.reserved = { 0 },};extern "C" __attribute__((constructor)) void RegisterEntryModule(void){napi_module_register(&demoModule);} -
接口声明
// index.d.tsexport const createSendableRef: (a: object) => string;export const getAndModifySendableRefValueInArkRuntime: () => string;export const getSendableRefValueInWorkerOrTaskpool: () => object;export const checkAndDeleteSendableRef: () => string; -
ArkTS代码示例
// index.etsimport { hilog } from '@kit.PerformanceAnalysisKit';import testNapi from 'libentry.so';import { MessageEvents, taskpool, worker} from '@kit.ArkTS'const DOMAIN = 0x0000;@Sendableclass SendableClass {num: number = 1111;}@Concurrentfunction TaskpoolFunc(data: string) {console.info('testTag, ' + data);let sObj = testNapi.getSendableRefValueInWorkerOrTaskpool() as SendableClass;sObj.num = 2222;}async function concurrentFunc() {let sObj = new SendableClass();hilog.info(DOMAIN, 'testTag', 'Test CreateSendableRef result = %{public}s',testNapi.createSendableRef(sObj));const task: taskpool.Task = new taskpool.Task(TaskpoolFunc,'Please check sendable ref value in taskpool thread');await taskpool.execute(task);let ret: string = testNapi.checkAndDeleteSendableRef();return ret;}@Entry@Componentstruct Index {@State TestMsg1: string = 'TestInArkRuntime';@State TestMsg2: string = 'TestInWorker';@State TestMsg3: string = 'TestInTaskpool';build() {Row() {Column() {Button(this.TestMsg1).fontSize($r('app.float.page_text_font_size')).fontWeight(FontWeight.Bold).onClick(() => {let sObj = new SendableClass();hilog.info(DOMAIN, 'testTag', 'Test CreateSendableRef result = %{public}s',testNapi.createSendableRef(sObj));hilog.info(DOMAIN, 'testTag', 'Test GetAndModifySendableRefValue result = %{public}s',testNapi.getAndModifySendableRefValueInArkRuntime());this.TestMsg1 = testNapi.checkAndDeleteSendableRef();})Button(this.TestMsg2).fontSize($r('app.float.page_text_font_size')).fontWeight(FontWeight.Bold).onClick(() => {let sObj = new SendableClass();hilog.info(DOMAIN, 'testTag', 'Test CreateSendableRef result = %{public}s',testNapi.createSendableRef(sObj));const worker1: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/Worker.ets');worker1.onmessage = (e: MessageEvents) => {let data: string = e.data;hilog.info(DOMAIN, 'testTag', data);this.TestMsg2 = testNapi.checkAndDeleteSendableRef();}worker1.postMessage('Please check sendable ref value in worker thread');})Button(this.TestMsg3).fontSize($r('app.float.page_text_font_size')).fontWeight(FontWeight.Bold).onClick(() => {concurrentFunc().then((ret) => {this.TestMsg3 = ret;});})}.width('100%')}.height('100%')}}// Worker.etsimport { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';import testNapi from 'libentry.so';import { hilog } from '@kit.PerformanceAnalysisKit';const DOMAIN = 0x0000;@Sendableclass SendableClass {num: number = 1111;}const workerPort: ThreadWorkerGlobalScope = worker.workerPort;/*** Defines the event handler to be called when the worker thread receives a message sent by the host thread.* The event handler is executed in the worker thread.** @param event message data*/workerPort.onmessage = (event: MessageEvents) => {let data: string = event.data;hilog.info(DOMAIN, 'testTag', data);let sObj = testNapi.getSendableRefValueInWorkerOrTaskpool() as SendableClass;sObj.num = 2222;workerPort.postMessage('Please check sendable ref value and delete ref');};/*** Defines the event handler to be called when the worker receives a message that cannot be deserialized.* The event handler is executed in the worker thread.** @param event message data*/workerPort.onmessageerror = (event: MessageEvents) => {};/*** Defines the event handler to be called when an exception occurs during worker execution.* The event handler is executed in the worker thread.** @param event error message*/workerPort.onerror = (event: ErrorEvent) => {};
注意事项
- 只能为Sendable对象创建napi_sendable_ref。
- napi_sendable_ref可跨ArkTS线程使用,在多线程操作时,调用者需自己保证释放时机,防止出现释放后使用的问题。
- 同一进程内,同时存活的napi_sendable_ref最大数量为51200个。
- napi_sendable_ref与其他引用的类型不同,因此不可将napi_ref、napi_strong_ref等其他引用强转成napi_sendable_ref。napi_delete_strong_sendable_reference和napi_get_strong_sendable_reference_value接口仅允许接收由napi_create_strong_sendable_reference创建的napi_sendable_ref。
- 在使用napi_create_strong_sendable_reference、napi_get_strong_sendable_reference_value和napi_delete_strong_sendable_reference接口时,调用者需要保证传入的env参数是当前调用接口的ArkTS线程环境对象,避免将其他ArkTS线程的env作为参数传入导致出现多线程安全问题。