1# Multithreaded Operations with Custom Native Sendable Objects 2 3ArkTS supports custom native Sendable objects, which provide efficient inter-thread communication through pass-by-reference. This is particularly valuable for scenarios requiring inter-thread communication of large custom objects, such as when a child thread retrieves database data and returns it to the host thread. 4 5The following demonstrates how to implement shared data access across concurrent instances using custom native Sendable objects. 6 71. Customize a Sendable class in the interface declaration. 8 9 ```ts 10 // Index.d.ts 11 @Sendable 12 export class MyObject { 13 constructor(arg: number); 14 plusOne(): number; 15 16 public get value(); 17 public set value(newVal: number); 18 } 19 ``` 20 212. Configure the build environment. 22 23 ```cmake 24 # CMakeLists.txt 25 # the minimum version of CMake. 26 cmake_minimum_required(VERSION 3.5.0) 27 project(napi_wrap_sendable_demo) 28 29 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 30 31 if(DEFINED PACKAGE_FIND_FILE) 32 include(${PACKAGE_FIND_FILE}) 33 endif() 34 35 include_directories(${NATIVERENDER_ROOT_PATH} 36 ${NATIVERENDER_ROOT_PATH}/include) 37 38 add_library(entry SHARED napi_init.cpp) 39 target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so) 40 ``` 41 423. Implement the features in native code, such as obtaining values, setting values, and incrementing values. 43 44 ```cpp 45 // napi_init.cpp 46 #include "napi/native_api.h" 47 #include "hilog/log.h" 48 49 // A native class. Its instance is wrapped in an ArkTS Sendable object. 50 class MyObject { 51 public: 52 static napi_value Init(napi_env env, napi_value exports); 53 static void Destructor(napi_env env, void *nativeObject, void *finalize_hint); 54 55 private: 56 explicit MyObject(double value_ = 0); 57 ~MyObject(); 58 59 static napi_value New(napi_env env, napi_callback_info info); 60 static napi_value GetValue(napi_env env, napi_callback_info info); 61 static napi_value SetValue(napi_env env, napi_callback_info info); 62 static napi_value PlusOne(napi_env env, napi_callback_info info); 63 64 double value_; 65 napi_env env_; 66 }; 67 68 static thread_local napi_ref g_ref = nullptr; 69 70 MyObject::MyObject(double value) : value_(value), env_(nullptr) {} 71 72 MyObject::~MyObject() {} 73 74 void MyObject::Destructor(napi_env env, void *nativeObject, [[maybe_unused]] void *finalize_hint) 75 { 76 OH_LOG_INFO(LOG_APP, "MyObject::Destructor called"); 77 reinterpret_cast<MyObject *>(nativeObject)->~MyObject(); 78 } 79 80 // Bind the ArkTS Sendable object to a C++ object within a constructor. 81 napi_value MyObject::New(napi_env env, napi_callback_info info) 82 { 83 OH_LOG_INFO(LOG_APP, "MyObject::New called"); 84 85 napi_value newTarget; 86 napi_get_new_target(env, info, &newTarget); 87 if (newTarget != nullptr) { 88 // Use the call mode new MyObject(...). 89 size_t argc = 1; 90 napi_value args[1]; 91 napi_value jsThis; 92 napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr); 93 94 double value = 0.0; 95 napi_valuetype valuetype; 96 napi_typeof(env, args[0], &valuetype); 97 if (valuetype != napi_undefined) { 98 napi_get_value_double(env, args[0], &value); 99 } 100 101 MyObject *obj = new MyObject(value); 102 103 obj->env_ = env; 104 // Use napi_wrap_sendable to bind the ArkTS Sendable object jsThis to the C++ object obj. 105 napi_wrap_sendable(env, jsThis, reinterpret_cast<void *>(obj), MyObject::Destructor, nullptr); 106 107 return jsThis; 108 } else { 109 // Use the call mode MyObject(...). 110 size_t argc = 1; 111 napi_value args[1]; 112 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 113 114 napi_value cons; 115 napi_get_reference_value(env, g_ref, &cons); 116 napi_value instance; 117 napi_new_instance(env, cons, argc, args, &instance); 118 119 return instance; 120 } 121 } 122 123 // Obtain the value of the native object. 124 napi_value MyObject::GetValue(napi_env env, napi_callback_info info) 125 { 126 OH_LOG_INFO(LOG_APP, "MyObject::GetValue called"); 127 128 napi_value jsThis; 129 napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); 130 131 MyObject *obj; 132 // Use napi_unwrap_sendable to retrieve obj (the C++ object) previously wrapped in jsThis (the ArkTS Sendable object), and perform subsequent operations. 133 napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj)); 134 napi_value num; 135 napi_create_double(env, obj->value_, &num); 136 137 return num; 138 } 139 140 // Set the value of the native object. 141 napi_value MyObject::SetValue(napi_env env, napi_callback_info info) 142 { 143 OH_LOG_INFO(LOG_APP, "MyObject::SetValue called"); 144 145 size_t argc = 1; 146 napi_value value; 147 napi_value jsThis; 148 149 napi_get_cb_info(env, info, &argc, &value, &jsThis, nullptr); 150 151 MyObject *obj; 152 // Use napi_unwrap_sendable to retrieve obj (the C++ object) previously wrapped in jsThis (the ArkTS Sendable object), and perform subsequent operations. 153 napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj)); 154 napi_get_value_double(env, value, &obj->value_); 155 156 return nullptr; 157 } 158 159 // Increase the value of the native object by 1. 160 napi_value MyObject::PlusOne(napi_env env, napi_callback_info info) 161 { 162 OH_LOG_INFO(LOG_APP, "MyObject::PlusOne called"); 163 164 napi_value jsThis; 165 napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr); 166 167 MyObject *obj; 168 // Use napi_unwrap_sendable to retrieve obj (the C++ object) previously wrapped in jsThis (the ArkTS Sendable object), and perform subsequent operations. 169 napi_unwrap_sendable(env, jsThis, reinterpret_cast<void **>(&obj)); 170 obj->value_ += 1; 171 napi_value num; 172 napi_create_double(env, obj->value_, &num); 173 174 return num; 175 } 176 177 napi_value MyObject::Init(napi_env env, napi_value exports) 178 { 179 napi_value num; 180 napi_create_double(env, 0, &num); 181 napi_property_descriptor properties[] = { 182 {"value", nullptr, nullptr, GetValue, SetValue, nullptr, napi_default, nullptr}, 183 {"plusOne", nullptr, PlusOne, nullptr, nullptr, nullptr, napi_default, nullptr}, 184 }; 185 186 napi_value cons; 187 // Define a Sendable class MyObject. 188 napi_define_sendable_class(env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 189 sizeof(properties) / sizeof(properties[0]), properties, nullptr, &cons); 190 191 napi_create_reference(env, cons, 1, &g_ref); 192 // Mount the MyObject class to the exports object. 193 napi_set_named_property(env, exports, "MyObject", cons); 194 return exports; 195 } 196 197 EXTERN_C_START 198 // Initialize the module. 199 static napi_value Init(napi_env env, napi_value exports) { 200 MyObject::Init(env, exports); 201 return exports; 202 } 203 EXTERN_C_END 204 205 // Information about the module. Record information such as the Init() function and module name. 206 static napi_module nativeModule = { 207 .nm_version = 1, 208 .nm_flags = 0, 209 .nm_filename = nullptr, 210 .nm_register_func = Init, 211 .nm_modname = "entry", 212 .nm_priv = nullptr, 213 .reserved = {0}, 214 }; 215 216 // This function is automatically called when the.so file is loaded to register the nativeModule module with the system. 217 extern "C" __attribute__((constructor)) void RegisterObjectWrapModule() { napi_module_register(&nativeModule); } 218 ``` 219 2204. On the ArkTS side, define a Sendable instance in the UI main thread and pass it to the TaskPool child thread. The child thread processes the data and returns the data to the UI main thread, which can continue to access the Sendable instance. 221 222 ```ts 223 // Index.ets 224 import { MyObject } from 'libentry.so'; 225 import { taskpool } from '@kit.ArkTS'; 226 227 @Concurrent 228 async function Sum(object: MyObject) { 229 object.value = 2000; 230 let num = object.plusOne(); 231 console.info("taskpool thread num is " + num); // taskpool thread num is 2001 232 return num; 233 } 234 235 @Entry 236 @Component 237 struct Index { 238 @State message: string = 'Hello World'; 239 240 build() { 241 Row() { 242 Column() { 243 Text(this.message) 244 .fontSize($r('app.float.page_text_font_size')) 245 .fontWeight(FontWeight.Bold) 246 .onClick( async () => { 247 let object : MyObject = new MyObject(0); 248 object.value = 1023; 249 let num = object.plusOne(); 250 console.info("host thread num1 is " + num); // host thread num1 is 1024 251 let task = new taskpool.Task(Sum, object); 252 let result = await taskpool.execute(task); 253 console.info("host thread result is " + result); // host thread result is 2001 254 console.info("host thread num2 is " + object.value); // host thread num2 is 2001 255 }) 256 } 257 .width('100%') 258 } 259 .height('100%') 260 } 261 } 262 ``` 263