使用扩展的Node-API接口创建对ArkTS对象的Sendable强引用
OpenHarmony的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; // 删除SendableRef napi_value str = nullptr; ASSERT_CHECK_CALL(napi_create_string_utf8(env, "success", NAPI_AUTO_LENGTH, &str)); return str; } EXTERN_C_START static 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_END static 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.ts export const createSendableRef: (a: object) => string; export const getAndModifySendableRefValueInArkRuntime: () => string; export const getSendableRefValueInWorkerOrTaskpool: () => object; export const checkAndDeleteSendableRef: () => string; -
ArkTS代码示例
// index.ets import { hilog } from '@kit.PerformanceAnalysisKit'; import testNapi from 'libentry.so'; import { MessageEvents, taskpool, worker} from '@kit.ArkTS' const DOMAIN = 0x0000; @Sendable class SendableClass { num: number = 1111; } @Concurrent function 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 @Component struct 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.ets import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; import testNapi from 'libentry.so'; import { hilog } from '@kit.PerformanceAnalysisKit'; const DOMAIN = 0x0000; @Sendable class 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_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。