1# 自定义Native Sendable对象的多线程操作场景 2<!--Kit: ArkTS--> 3<!--Subsystem: CommonLibrary--> 4<!--Owner: @lijiamin2025--> 5<!--Designer: @weng-changcheng--> 6<!--Tester: @kirl75; @zsw_zhushiwei--> 7<!--Adviser: @ge-yafang--> 8 9ArkTS支持开发者自定义Native Sendable对象,Sendable对象提供了并发实例间高效的通信能力,即引用传递,适用于开发者自定义大对象需要线程间通信的场景,例如子线程读取数据库数据并返回给宿主线程。 10 11本示例将详细说明如何使用自定义Native Sendable对象实现并发实例间数据共享。 12 131. 接口声明中自定义Sendable类。 14 15 ```ts 16 // Index.d.ets 17 @Sendable 18 export class MyObject { 19 constructor(arg: number); 20 plusOne(): number; 21 22 public get value(): number; 23 public set value(newVal: number); 24 } 25 ``` 26 272. 编译配置。 28 29 ```cmake 30 # CMakeLists.txt 31 # the minimum version of CMake. 32 cmake_minimum_required(VERSION 3.5.0) 33 project(napi_wrap_sendable_demo) 34 35 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 36 37 if(DEFINED PACKAGE_FIND_FILE) 38 include(${PACKAGE_FIND_FILE}) 39 endif() 40 41 include_directories(${NATIVERENDER_ROOT_PATH} 42 ${NATIVERENDER_ROOT_PATH}/include) 43 44 add_library(entry SHARED napi_init.cpp) 45 target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so) 46 ``` 47 483. Native实现各项接口功能,例如取值、设置值或者给Native对象的值加1等功能。 49 50 ```cpp 51 // napi_init.cpp 52 #include "napi/native_api.h" 53 #include "hilog/log.h" 54 55 // 一个native类,它的实例在下面会包装在ArkTS的Sendable对象中 56 class MyObject { 57 public: 58 static napi_value Init(napi_env env, napi_value exports); 59 static void Destructor(napi_env env, void *nativeObject, void *finalize_hint); 60 61 private: 62 explicit MyObject(double value_ = 0); 63 ~MyObject(); 64 65 static napi_value New(napi_env env, napi_callback_info info); 66 static napi_value GetValue(napi_env env, napi_callback_info info); 67 static napi_value SetValue(napi_env env, napi_callback_info info); 68 static napi_value PlusOne(napi_env env, napi_callback_info info); 69 70 double value_; 71 napi_env env_; 72 }; 73 74 static thread_local napi_ref g_ref = nullptr; 75 76 MyObject::MyObject(double value) : value_(value), env_(nullptr) {} 77 78 MyObject::~MyObject() {} 79 80 void MyObject::Destructor(napi_env env, void *nativeObject, [[maybe_unused]] void *finalize_hint) 81 { 82 OH_LOG_INFO(LOG_APP, "MyObject::Destructor called"); 83 if (g_ref != nullptr) { 84 napi_delete_reference(env, g_ref); 85 g_ref = nullptr; 86 } 87 reinterpret_cast<MyObject *>(nativeObject)->~MyObject(); 88 } 89 90 // 在构造函数中绑定ArkTS Sendable对象与C++对象 91 napi_value MyObject::New(napi_env env, napi_callback_info info) 92 { 93 OH_LOG_INFO(LOG_APP, "MyObject::New called"); 94 95 napi_value newTarget; 96 napi_get_new_target(env, info, &newTarget); 97 if (newTarget != nullptr) { 98 // 使用`new MyObject(...)`调用方式 99 size_t argc = 1; 100 napi_value args[1]; 101 napi_value jsThis; 102 napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr); 103 104 double value = 0.0; 105 napi_valuetype valuetype; 106 napi_typeof(env, args[0], &valuetype); 107 if (valuetype != napi_undefined) { 108 napi_get_value_double(env, args[0], &value); 109 } 110 111 MyObject *obj = new MyObject(value); 112 113 obj->env_ = env; 114 // 通过napi_wrap_sendable将ArkTS Sendable对象jsThis与C++对象obj绑定 115 napi_wrap_sendable(env, jsThis, reinterpret_cast<void *>(obj), MyObject::Destructor, nullptr); 116 117 return jsThis; 118 } else { 119 // 使用`MyObject(...)`调用方式 120 size_t argc = 1; 121 napi_value args[1]; 122 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 123 124 napi_value cons; 125 napi_get_reference_value(env, g_ref, &cons); 126 napi_value instance; 127 napi_new_instance(env, cons, argc, args, &instance); 128 129 return instance; 130 } 131 } 132 133 // 取出Native对象的值 134 napi_value MyObject::GetValue(napi_env env, napi_callback_info info) 135 { 136 OH_LOG_INFO(LOG_APP, "MyObject::GetValue called"); 137 138 napi_value jsThis; 139 napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); 140 141 MyObject *obj; 142 // 通过napi_unwrap_sendable将jsThis之前绑定的C++对象取出,并对其进行操作 143 napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj)); 144 napi_value num; 145 napi_create_double(env, obj->value_, &num); 146 147 return num; 148 } 149 150 // 设置Native对象的值 151 napi_value MyObject::SetValue(napi_env env, napi_callback_info info) 152 { 153 OH_LOG_INFO(LOG_APP, "MyObject::SetValue called"); 154 155 size_t argc = 1; 156 napi_value value; 157 napi_value jsThis; 158 159 napi_get_cb_info(env, info, &argc, &value, &jsThis, nullptr); 160 161 MyObject *obj; 162 // 通过napi_unwrap_sendable将jsThis之前绑定的C++对象取出,并对其进行操作 163 napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj)); 164 napi_get_value_double(env, value, &obj->value_); 165 166 return nullptr; 167 } 168 169 // 给Native对象的值加1 170 napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) 171 { 172 OH_LOG_INFO(LOG_APP, "MyObject::PlusOne called"); 173 174 napi_value jsThis; 175 napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); 176 177 MyObject *obj; 178 // 通过napi_unwrap_sendable将jsThis之前绑定的C++对象取出,并对其进行操作 179 napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj)); 180 obj->value_ += 1; 181 napi_value num; 182 napi_create_double(env, obj->value_, &num); 183 184 return num; 185 } 186 187 napi_value MyObject::Init(napi_env env, napi_value exports) 188 { 189 napi_value num; 190 napi_create_double(env, 0, &num); 191 napi_property_descriptor properties[] = { 192 {"value", nullptr, nullptr, GetValue, SetValue, nullptr, napi_default, nullptr}, 193 {"plusOne", nullptr, PlusOne, nullptr, nullptr, nullptr, napi_default, nullptr}, 194 }; 195 196 napi_value cons; 197 // 定义一个Sendable class MyObject 198 napi_define_sendable_class(env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 199 sizeof(properties) / sizeof(properties[0]), properties, nullptr, &cons); 200 201 napi_create_reference(env, cons, 1, &g_ref); 202 // 在exports对象上挂载MyObject类 203 napi_set_named_property(env, exports, "MyObject", cons); 204 return exports; 205 } 206 207 EXTERN_C_START 208 // 模块初始化 209 static napi_value Init(napi_env env, napi_value exports) { 210 MyObject::Init(env, exports); 211 return exports; 212 } 213 EXTERN_C_END 214 215 // 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。 216 static napi_module nativeModule = { 217 .nm_version = 1, 218 .nm_flags = 0, 219 .nm_filename = nullptr, 220 .nm_register_func = Init, 221 .nm_modname = "entry", 222 .nm_priv = nullptr, 223 .reserved = {0}, 224 }; 225 226 // 加载so时,自动调用该函数,将上述nativeModule模块注册到系统中。 227 extern "C" __attribute__((constructor)) void RegisterObjectWrapModule() { napi_module_register(&nativeModule); } 228 ``` 229 2304. ArkTS侧在UI主线程中定义Sendable实例对象并传递给TaskPool子线程,子线程处理完数据后返回UI主线程,UI主线程可以继续访问该Sendable实例对象。 231 232 ```ts 233 // Index.ets 234 import { MyObject } from 'libentry.so'; 235 import { taskpool } from '@kit.ArkTS'; 236 237 @Concurrent 238 async function Sum(object: MyObject) { 239 object.value = 2000; 240 let num = object.plusOne(); 241 console.info("taskpool thread num is " + num); // taskpool thread num is 2001 242 return num; 243 } 244 245 @Entry 246 @Component 247 struct Index { 248 @State message: string = 'Hello World'; 249 250 build() { 251 Row() { 252 Column() { 253 Text(this.message) 254 .fontSize($r('app.float.page_text_font_size')) 255 .fontWeight(FontWeight.Bold) 256 .onClick( async () => { 257 let object : MyObject = new MyObject(0); 258 object.value = 1023; 259 let num = object.plusOne(); 260 console.info("host thread num1 is " + num); // host thread num1 is 1024 261 let task = new taskpool.Task(Sum, object); 262 let result = await taskpool.execute(task); 263 console.info("host thread result is " + result); // host thread result is 2001 264 console.info("host thread num2 is " + object.value); // host thread num2 is 2001 265 }) 266 } 267 .width('100%') 268 } 269 .height('100%') 270 } 271 } 272 ``` 2735. 修改与Index.d.ets同目录下的配置文件oh-package.json5,配置如下: 274 ```ts 275 { 276 "name": "libentry.so", 277 "types": "./Index.d.ets", 278 "version": "1.0.0", 279 "description": "Please describe the basic information." 280 } 281 ``` 282