• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义Native Transferable对象的多线程操作场景
2<!--Kit: ArkTS-->
3<!--Subsystem: CommonLibrary-->
4<!--Owner: @lijiamin2025-->
5<!--Designer: @weng-changcheng-->
6<!--Tester: @kirl75; @zsw_zhushiwei-->
7<!--Adviser: @ge-yafang-->
8
9在ArkTS应用开发中,有很多场景需要将ArkTS对象与Native对象进行绑定。ArkTS对象将数据写入Native对象,Native对象再将数据写入目的地。例如,将ArkTS对象中的数据写入C++数据库场景。
10
11Native Transferable对象有两种模式:共享模式和转移模式。本示例将详细说明如何实现这两种模式。
12
131. Native实现各项功能。
14
15   ```cpp
16   // napi_init.cpp
17   #include <mutex>
18   #include <unordered_set>
19   #include "napi/native_api.h"
20   #include <hilog/log.h>
21
22   class CustomNativeObject {
23   public:
24       CustomNativeObject() {}
25       ~CustomNativeObject() = default;
26       static CustomNativeObject& GetInstance()
27       {
28           static CustomNativeObject instance;
29           return instance;
30       }
31
32       static napi_value GetAddress(napi_env env, napi_callback_info info)
33       {
34           napi_value thisVar = nullptr;
35           napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
36           if (thisVar == nullptr) {
37               return nullptr;
38           }
39           void* object = nullptr;
40           napi_unwrap(env, thisVar, &object);
41           if (object == nullptr) {
42               return nullptr;
43           }
44
45           uint64_t addressVal = reinterpret_cast<uint64_t>(object);
46           napi_value address = nullptr;
47           napi_create_bigint_uint64(env, addressVal, &address);
48           return address;
49       }
50
51       // 获取数组大小
52       static napi_value GetSetSize(napi_env env, napi_callback_info info)
53       {
54           napi_value thisVar = nullptr;
55           napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
56           if (thisVar == nullptr) {
57               return nullptr;
58           }
59           void* object = nullptr;
60           napi_unwrap(env, thisVar, &object);
61           if (object == nullptr) {
62               return nullptr;
63           }
64           CustomNativeObject* obj = static_cast<CustomNativeObject*>(object);
65           std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
66           uint32_t setSize = reinterpret_cast<CustomNativeObject*>(object)->numberSet_.size();
67           napi_value napiSize = nullptr;
68           napi_create_uint32(env, setSize, &napiSize);
69           return napiSize;
70       }
71
72       // 往数组里插入元素
73       static napi_value Store(napi_env env, napi_callback_info info)
74       {
75           size_t argc = 1;
76           napi_value args[1] = {nullptr};
77           napi_value thisVar = nullptr;
78           napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
79           if (argc != 1) {
80               napi_throw_error(env, nullptr, "Store args number must be one.");
81               return nullptr;
82           }
83           napi_valuetype type = napi_undefined;
84           napi_typeof(env, args[0], &type);
85           if (type != napi_number) {
86               napi_throw_error(env, nullptr, "Store args is not number.");
87               return nullptr;
88           }
89           if (thisVar == nullptr) {
90               return nullptr;
91           }
92
93           void* object = nullptr;
94           napi_unwrap(env, thisVar, &object);
95           if (object == nullptr) {
96               return nullptr;
97           }
98
99           uint32_t value = 0;
100           napi_get_value_uint32(env, args[0], &value);
101           CustomNativeObject* obj = static_cast<CustomNativeObject*>(object);
102           std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
103           reinterpret_cast<CustomNativeObject *>(object)->numberSet_.insert(value);
104           return nullptr;
105       }
106
107       // 删除数组元素
108       static napi_value Erase(napi_env env, napi_callback_info info)
109       {
110           size_t argc = 1;
111           napi_value args[1] = {nullptr};
112           napi_value thisVar = nullptr;
113           napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
114           if (argc != 1) {
115               napi_throw_error(env, nullptr, "Erase args number must be one.");
116               return nullptr;
117           }
118           napi_valuetype type = napi_undefined;
119           napi_typeof(env, args[0], &type);
120           if (type != napi_number) {
121               napi_throw_error(env, nullptr, "Erase args is not number.");
122               return nullptr;
123           }
124           if (thisVar == nullptr) {
125               return nullptr;
126           }
127
128           void* object = nullptr;
129           napi_unwrap(env, thisVar, &object);
130           if (object == nullptr) {
131               return nullptr;
132           }
133
134           uint32_t value = 0;
135           napi_get_value_uint32(env, args[0], &value);
136
137           CustomNativeObject* obj = static_cast<CustomNativeObject*>(object);
138           std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
139           reinterpret_cast<CustomNativeObject *>(object)->numberSet_.erase(value);
140           return nullptr;
141       }
142
143       // 清空数组
144       static napi_value Clear(napi_env env, napi_callback_info info)
145       {
146           napi_value thisVar = nullptr;
147           napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
148           if (thisVar == nullptr) {
149               return nullptr;
150           }
151           void* object = nullptr;
152           napi_unwrap(env, thisVar, &object);
153           if (object == nullptr) {
154               return nullptr;
155           }
156           CustomNativeObject* obj = static_cast<CustomNativeObject*>(object);
157           std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
158           reinterpret_cast<CustomNativeObject *>(object)->numberSet_.clear();
159           return nullptr;
160       }
161
162       // 设置传输模式
163       static napi_value SetTransferDetached(napi_env env, napi_callback_info info)
164       {
165           size_t argc = 1;
166           napi_value args[1];
167           napi_value thisVar;
168           napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
169           if (argc != 1) {
170               napi_throw_error(env, nullptr, "SetTransferDetached args number must be one.");
171               return nullptr;
172           }
173
174           if (thisVar == nullptr) {
175               return nullptr;
176           }
177
178           napi_valuetype type = napi_undefined;
179           napi_typeof(env, args[0], &type);
180           if (type != napi_boolean) {
181               napi_throw_error(env, nullptr, "SetTransferDetached args is not boolean.");
182               return nullptr;
183           }
184
185           bool isDetached;
186           napi_get_value_bool(env, args[0], &isDetached);
187
188           void* object = nullptr;
189           napi_unwrap(env, thisVar, &object);
190           if (object == nullptr) {
191               return nullptr;
192           }
193           CustomNativeObject* obj = static_cast<CustomNativeObject*>(object);
194           std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
195           obj->isDetached_ = isDetached;
196           return nullptr;
197       }
198
199       bool isDetached_ = false;
200
201   private:
202       CustomNativeObject(const CustomNativeObject &) = delete;
203       CustomNativeObject &operator=(const CustomNativeObject &) = delete;
204
205       std::unordered_set<uint32_t> numberSet_{};
206       std::mutex numberSetMutex_{};
207   };
208
209   void FinializeCallback(napi_env env, void *data, void *hint)
210   {
211       return;
212   }
213
214   // 解绑回调,在序列化时调用,可在对象解绑时执行一些清理操作
215   void* DetachCallback(napi_env env, void *value, void *hint)
216   {
217       if (hint == nullptr) {
218           return value;
219       }
220       napi_value jsObject = nullptr;
221       napi_get_reference_value(env, reinterpret_cast<napi_ref>(hint), &jsObject);
222       void* object = nullptr;
223       if (static_cast<CustomNativeObject*>(value)->isDetached_) {
224           napi_remove_wrap(env, jsObject, &object);
225       }
226       return value;
227   }
228
229   // 绑定回调,在反序列化时调用
230   napi_value AttachCallback(napi_env env, void* value, void* hint)
231   {
232       napi_value object = nullptr;
233       napi_create_object(env, &object);
234       napi_property_descriptor desc[] = {
235           {"getAddress", nullptr, CustomNativeObject::GetAddress, nullptr, nullptr, nullptr, napi_default, nullptr},
236           {"getSetSize", nullptr, CustomNativeObject::GetSetSize, nullptr, nullptr, nullptr, napi_default, nullptr},
237           {"store", nullptr, CustomNativeObject::Store, nullptr, nullptr, nullptr, napi_default, nullptr},
238           {"erase", nullptr, CustomNativeObject::Erase, nullptr, nullptr, nullptr, napi_default, nullptr},
239           {"clear", nullptr, CustomNativeObject::Clear, nullptr, nullptr, nullptr, napi_default, nullptr}};
240       napi_define_properties(env, object, sizeof(desc) / sizeof(desc[0]), desc);
241       // 将JS对象object和native对象value生命周期进行绑定
242       napi_wrap(env, object, value, FinializeCallback, nullptr, nullptr);
243       // JS对象携带native信息
244       napi_coerce_to_native_binding_object(env, object, DetachCallback, AttachCallback, value, nullptr);
245       return object;
246   }
247
248   EXTERN_C_START
249   static napi_value Init(napi_env env, napi_value exports)
250   {
251       napi_property_descriptor desc[] = {
252           {"getAddress", nullptr, CustomNativeObject::GetAddress, nullptr, nullptr, nullptr, napi_default, nullptr},
253           {"getSetSize", nullptr, CustomNativeObject::GetSetSize, nullptr, nullptr, nullptr, napi_default, nullptr},
254           {"store", nullptr, CustomNativeObject::Store, nullptr, nullptr, nullptr, napi_default, nullptr},
255           {"erase", nullptr, CustomNativeObject::Erase, nullptr, nullptr, nullptr, napi_default, nullptr},
256           {"clear", nullptr, CustomNativeObject::Clear, nullptr, nullptr, nullptr, napi_default, nullptr},
257           {"setTransferDetached", nullptr, CustomNativeObject::SetTransferDetached, nullptr, nullptr, nullptr, napi_default, nullptr}};
258       napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
259       auto &object = CustomNativeObject::GetInstance();
260       napi_wrap(env, exports, reinterpret_cast<void*>(&object), FinializeCallback, nullptr, nullptr);
261       napi_ref exportsRef;
262       napi_create_reference(env, exports, 1, &exportsRef);
263       napi_coerce_to_native_binding_object(env, exports, DetachCallback, AttachCallback, reinterpret_cast<void*>(&object), exportsRef);
264       return exports;
265   }
266   EXTERN_C_END
267
268   static napi_module demoModule = {
269       .nm_version = 1,
270       .nm_flags = 0,
271       .nm_filename = nullptr,
272       .nm_register_func = Init,
273       .nm_modname = "entry",
274       .nm_priv = ((void*)0),
275       .reserved = { 0 },
276   };
277
278   extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
279   {
280       napi_module_register(&demoModule);
281   }
282   ```
283
284
2852. 在ArkTS中声明接口。
286
287   ```ts
288   // Index.d.ts
289   export const getAddress: () => number;
290   export const getSetSize: () => number;
291   export const store: (a: number) => void;
292   export const erase: (a: number) => void;
293   export const clear: () => void;
294   export const setTransferDetached: (b : boolean) => number;
295   ```
296
2973. ArkTS对象调用Native侧实现的各项功能。
298
299   在转移模式下,跨线程传递后,原来的ArkTS对象与Native对象解绑,因此不能继续访问。示例如下:
300   ```ts
301   import testNapi from 'libentry.so';
302   import { taskpool } from '@kit.ArkTS';
303
304   @Concurrent
305   function getAddress() {
306     let address: number = testNapi.getAddress();
307     console.info("taskpool:: address is " + address);
308   }
309
310   @Concurrent
311   function store(a:number, b:number, c:number) {
312     let size:number = testNapi.getSetSize();
313     console.info("set size is " + size + " before store");
314     testNapi.store(a);
315     testNapi.store(b);
316     testNapi.store(c);
317     size = testNapi.getSetSize();
318     console.info("set size is " + size + " after store");
319   }
320
321   @Concurrent
322   function erase(a:number) {
323     let size:number = testNapi.getSetSize();
324     console.info("set size is " + size + " before erase");
325     testNapi.erase(a);
326     size = testNapi.getSetSize();
327     console.info("set size is " + size + " after erase");
328   }
329
330   @Concurrent
331   function clear() {
332     let size:number = testNapi.getSetSize();
333     console.info("set size is " + size + " before clear");
334     testNapi.clear();
335     size = testNapi.getSetSize();
336     console.info("set size is " + size + " after clear");
337   }
338
339   // 转移模式
340   async function test(): Promise<void> {
341     // setTransferDetached 设置为true,表示传输方式为转移模式
342     testNapi.setTransferDetached(true);
343     let address:number = testNapi.getAddress();
344     console.info("host thread address is " + address);
345
346     let task1 = new taskpool.Task(getAddress, testNapi);
347     await taskpool.execute(task1);
348
349     let task2 = new taskpool.Task(store, 1, 2, 3);
350     await taskpool.execute(task2);
351
352     let task3 = new taskpool.Task(store, 4, 5, 6);
353     await taskpool.execute(task3);
354
355     // 由于已经设置了转移模式,且testNapi已跨线程传递,所以主线程无法继续访问到Native对象的值
356     let size:number = testNapi.getSetSize();
357     // 输出的日志为“host thread size is undefined”
358     console.info("host thread size is " + size);
359
360     let task4 = new taskpool.Task(erase, 3);
361     await taskpool.execute(task4);
362
363     let task5 = new taskpool.Task(erase, 5);
364     await taskpool.execute(task5);
365
366     let task6 = new taskpool.Task(clear);
367     await taskpool.execute(task6);
368   }
369
370   @Entry
371   @Component
372   struct Index {
373     @State message: string = 'Hello World';
374
375     build() {
376       Row() {
377         Column() {
378           Text(this.message)
379             .fontSize($r('app.float.page_text_font_size'))
380             .fontWeight(FontWeight.Bold)
381             .onClick(() => {
382               test();
383             })
384         }
385         .width('100%')
386       }
387       .height('100%')
388     }
389   }
390   ```
391
392   在共享模式下,跨线程传递后,原来的ArkTS对象还可以继续访问Native对象。示例如下:
393   ```ts
394   // Index.ets
395   import testNapi from 'libentry.so';
396   import { taskpool } from '@kit.ArkTS';
397
398   @Concurrent
399   function getAddress() {
400     let address: number = testNapi.getAddress();
401     console.info("taskpool:: address is " + address);
402   }
403
404   @Concurrent
405   function store(a:number, b:number, c:number) {
406     let size:number = testNapi.getSetSize();
407     console.info("set size is " + size + " before store");
408     testNapi.store(a);
409     testNapi.store(b);
410     testNapi.store(c);
411     size = testNapi.getSetSize();
412     console.info("set size is " + size + " after store");
413   }
414
415   @Concurrent
416   function erase(a:number) {
417     let size:number = testNapi.getSetSize();
418     console.info("set size is " + size + " before erase");
419     testNapi.erase(a);
420     size = testNapi.getSetSize();
421     console.info("set size is " + size + " after erase");
422   }
423
424   @Concurrent
425   function clear() {
426     let size:number = testNapi.getSetSize();
427     console.info("set size is " + size + " before clear");
428     testNapi.clear();
429     size = testNapi.getSetSize();
430     console.info("set size is " + size + " after clear");
431   }
432
433   // 共享模式
434   async function test(): Promise<void> {
435     let address:number = testNapi.getAddress();
436     console.info("host thread address is " + address);
437
438     let task1 = new taskpool.Task(getAddress, testNapi);
439     await taskpool.execute(task1);
440
441     let task2 = new taskpool.Task(store, 1, 2, 3);
442     await taskpool.execute(task2);
443
444     let task3 = new taskpool.Task(store, 4, 5, 6);
445     await taskpool.execute(task3);
446
447     // 由于默认的传输模式为共享模式,testNapi跨线程传递后,主线程可以继续访问Native对象的值
448     let size:number = testNapi.getSetSize();
449     // 输出的日志为“host thread size is 6”
450     console.info("host thread size is " + size);
451
452     let task4 = new taskpool.Task(erase, 3);
453     await taskpool.execute(task4);
454
455     let task5 = new taskpool.Task(erase, 5);
456     await taskpool.execute(task5);
457
458     let task6 = new taskpool.Task(clear);
459     await taskpool.execute(task6);
460   }
461
462   @Entry
463   @Component
464   struct Index {
465     @State message: string = 'Hello World';
466
467     build() {
468       Row() {
469         Column() {
470           Text(this.message)
471             .fontSize($r('app.float.page_text_font_size'))
472             .fontWeight(FontWeight.Bold)
473             .onClick(() => {
474               test();
475             })
476         }
477         .width('100%')
478       }
479       .height('100%')
480     }
481   }
482   ```