文档管理中心

自定义Native Transferable对象的多线程操作场景

在ArkTS应用开发中,有很多场景需要将ArkTS对象与Native对象进行绑定。ArkTS对象将数据写入Native对象,Native对象再将数据写入目的地。例如,将ArkTS对象中的数据写入C++数据库场景。

Native Transferable对象有两种模式:共享模式和转移模式。本示例将详细说明如何实现这两种模式。

  1. Native实现各项功能。

    收起
    自动换行
    深色代码主题
    复制
    1. // napi_init.cpp
    2. #include <mutex>
    3. #include <unordered_set>
    4. #include "napi/native_api.h"
    5. #include <hilog/log.h>
    6. class CustomNativeObject {
    7. public:
    8. CustomNativeObject() {}
    9. ~CustomNativeObject() = default;
    10. static CustomNativeObject& GetInstance()
    11. {
    12. static CustomNativeObject instance;
    13. return instance;
    14. }
    15. static napi_value GetAddress(napi_env env, napi_callback_info info)
    16. {
    17. napi_value thisVar = nullptr;
    18. napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
    19. if (thisVar == nullptr) {
    20. return nullptr;
    21. }
    22. void *object = nullptr;
    23. napi_unwrap(env, thisVar, &object);
    24. if (object == nullptr) {
    25. return nullptr;
    26. }
    27. uint64_t addressVal = reinterpret_cast<uint64_t>(object);
    28. napi_value address = nullptr;
    29. napi_create_bigint_uint64(env, addressVal, &address);
    30. return address;
    31. }
    32. // 获取数组大小
    33. static napi_value GetSetSize(napi_env env, napi_callback_info info)
    34. {
    35. napi_value thisVar = nullptr;
    36. napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
    37. if (thisVar == nullptr) {
    38. return nullptr;
    39. }
    40. void *object = nullptr;
    41. napi_unwrap(env, thisVar, &object);
    42. if (object == nullptr) {
    43. return nullptr;
    44. }
    45. CustomNativeObject *obj = static_cast<CustomNativeObject*>(object);
    46. std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
    47. uint32_t setSize = reinterpret_cast<CustomNativeObject *>(object)->numberSet_.size();
    48. napi_value napiSize = nullptr;
    49. napi_create_uint32(env, setSize, &napiSize);
    50. return napiSize;
    51. }
    52. // 往数组里插入元素
    53. static napi_value Store(napi_env env, napi_callback_info info)
    54. {
    55. size_t argc = 1;
    56. napi_value args[1] = {nullptr};
    57. napi_value thisVar = nullptr;
    58. napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
    59. if (argc != 1) {
    60. napi_throw_error(env, nullptr, "Store args number must be one.");
    61. return nullptr;
    62. }
    63. napi_valuetype type = napi_undefined;
    64. napi_typeof(env, args[0], &type);
    65. if (type != napi_number) {
    66. napi_throw_error(env, nullptr, "Store args is not number.");
    67. return nullptr;
    68. }
    69. if (thisVar == nullptr) {
    70. return nullptr;
    71. }
    72. void *object = nullptr;
    73. napi_unwrap(env, thisVar, &object);
    74. if (object == nullptr) {
    75. return nullptr;
    76. }
    77. uint32_t value = 0;
    78. napi_get_value_uint32(env, args[0], &value);
    79. CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
    80. std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
    81. reinterpret_cast<CustomNativeObject *>(object)->numberSet_.insert(value);
    82. return nullptr;
    83. }
    84. // 删除数组元素
    85. static napi_value Erase(napi_env env, napi_callback_info info)
    86. {
    87. size_t argc = 1;
    88. napi_value args[1] = {nullptr};
    89. napi_value thisVar = nullptr;
    90. napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
    91. if (argc != 1) {
    92. napi_throw_error(env, nullptr, "Erase args number must be one.");
    93. return nullptr;
    94. }
    95. napi_valuetype type = napi_undefined;
    96. napi_typeof(env, args[0], &type);
    97. if (type != napi_number) {
    98. napi_throw_error(env, nullptr, "Erase args is not number.");
    99. return nullptr;
    100. }
    101. if (thisVar == nullptr) {
    102. return nullptr;
    103. }
    104. void *object = nullptr;
    105. napi_unwrap(env, thisVar, &object);
    106. if (object == nullptr) {
    107. return nullptr;
    108. }
    109. uint32_t value = 0;
    110. napi_get_value_uint32(env, args[0], &value);
    111. CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
    112. std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
    113. reinterpret_cast<CustomNativeObject *>(object)->numberSet_.erase(value);
    114. return nullptr;
    115. }
    116. // 清空数组
    117. static napi_value Clear(napi_env env, napi_callback_info info)
    118. {
    119. napi_value thisVar = nullptr;
    120. napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
    121. if (thisVar == nullptr) {
    122. return nullptr;
    123. }
    124. void *object = nullptr;
    125. napi_unwrap(env, thisVar, &object);
    126. if (object == nullptr) {
    127. return nullptr;
    128. }
    129. CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
    130. std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
    131. reinterpret_cast<CustomNativeObject *>(object)->numberSet_.clear();
    132. return nullptr;
    133. }
    134. // 设置传输模式
    135. static napi_value SetTransferDetached(napi_env env, napi_callback_info info)
    136. {
    137. size_t argc = 1;
    138. napi_value args[1];
    139. napi_value thisVar;
    140. napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
    141. if (argc != 1) {
    142. napi_throw_error(env, nullptr, "SetTransferDetached args number must be one.");
    143. return nullptr;
    144. }
    145. if (thisVar == nullptr) {
    146. return nullptr;
    147. }
    148. napi_valuetype type = napi_undefined;
    149. napi_typeof(env, args[0], &type);
    150. if (type != napi_boolean) {
    151. napi_throw_error(env, nullptr, "SetTransferDetached args is not boolean.");
    152. return nullptr;
    153. }
    154. bool isDetached;
    155. napi_get_value_bool(env, args[0], &isDetached);
    156. void *object = nullptr;
    157. napi_unwrap(env, thisVar, &object);
    158. if (object == nullptr) {
    159. return nullptr;
    160. }
    161. CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
    162. std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
    163. obj->isDetached_ = isDetached;
    164. return nullptr;
    165. }
    166. bool isDetached_ = false;
    167. std::mutex numberSetMutex_{};
    168. private:
    169. CustomNativeObject(const CustomNativeObject &) = delete;
    170. CustomNativeObject &operator=(const CustomNativeObject &) = delete;
    171. std::unordered_set<uint32_t> numberSet_{};
    172. };
    173. void FinalizeCallback(napi_env env, void *data, void *hint)
    174. {
    175. return;
    176. }
    177. // 解绑回调,在序列化时调用,可在对象解绑时执行一些清理操作
    178. void* DetachCallback(napi_env env, void *value, void *hint)
    179. {
    180. if (hint == nullptr) {
    181. return value;
    182. }
    183. CustomNativeObject *obj = static_cast<CustomNativeObject *>(value);
    184. // 加锁保护 isDetached_ 的读取,与 SetTransferDetached 的写入互斥
    185. bool isDetached = false;
    186. {
    187. std::lock_guard<std::mutex> lock(obj->numberSetMutex_); // 需要std::mutex numberSetMutex_{};改为public
    188. isDetached = obj->isDetached_;
    189. }
    190. if (isDetached) {
    191. napi_value jsObject = nullptr;
    192. napi_get_reference_value(env, reinterpret_cast<napi_ref>(hint), &jsObject);
    193. void *object = nullptr;
    194. napi_remove_wrap(env, jsObject, &object);
    195. }
    196. return value;
    197. }
    198. // 绑定回调,在反序列化时调用
    199. napi_value AttachCallback(napi_env env, void* value, void* hint)
    200. {
    201. napi_value object = nullptr;
    202. napi_create_object(env, &object);
    203. napi_property_descriptor desc[] = {
    204. {"getAddress", nullptr, CustomNativeObject::GetAddress, nullptr, nullptr, nullptr, napi_default, nullptr},
    205. {"getSetSize", nullptr, CustomNativeObject::GetSetSize, nullptr, nullptr, nullptr, napi_default, nullptr},
    206. {"store", nullptr, CustomNativeObject::Store, nullptr, nullptr, nullptr, napi_default, nullptr},
    207. {"erase", nullptr, CustomNativeObject::Erase, nullptr, nullptr, nullptr, napi_default, nullptr},
    208. {"clear", nullptr, CustomNativeObject::Clear, nullptr, nullptr, nullptr, napi_default, nullptr}};
    209. napi_define_properties(env, object, sizeof(desc) / sizeof(desc[0]), desc);
    210. // 将JS对象object和native对象value生命周期进行绑定
    211. napi_wrap(env, object, value, FinalizeCallback, nullptr, nullptr);
    212. // 创建指向新 JS 对象的强引用
    213. napi_ref objectRef = nullptr;
    214. napi_create_reference(env, object, 1, &objectRef);
    215. // JS对象携带native信息
    216. napi_coerce_to_native_binding_object(env, object, DetachCallback, AttachCallback, value, objectRef);
    217. return object;
    218. }
    219. EXTERN_C_START
    220. static napi_value Init(napi_env env, napi_value exports)
    221. {
    222. napi_property_descriptor desc[] = {
    223. {"getAddress", nullptr, CustomNativeObject::GetAddress, nullptr, nullptr, nullptr, napi_default, nullptr},
    224. {"getSetSize", nullptr, CustomNativeObject::GetSetSize, nullptr, nullptr, nullptr, napi_default, nullptr},
    225. {"store", nullptr, CustomNativeObject::Store, nullptr, nullptr, nullptr, napi_default, nullptr},
    226. {"erase", nullptr, CustomNativeObject::Erase, nullptr, nullptr, nullptr, napi_default, nullptr},
    227. {"clear", nullptr, CustomNativeObject::Clear, nullptr, nullptr, nullptr, napi_default, nullptr},
    228. {"setTransferDetached", nullptr, CustomNativeObject::SetTransferDetached,
    229. nullptr, nullptr, nullptr, napi_default, nullptr}};
    230. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    231. auto &object = CustomNativeObject::GetInstance();
    232. napi_wrap(env, exports, reinterpret_cast<void *>(&object), FinalizeCallback, nullptr, nullptr);
    233. napi_ref exportsRef;
    234. napi_create_reference(env, exports, 1, &exportsRef);
    235. napi_coerce_to_native_binding_object(env, exports, DetachCallback,
    236. AttachCallback, reinterpret_cast<void *>(&object), exportsRef);
    237. return exports;
    238. }
    239. EXTERN_C_END
    240. static napi_module demoModule = {
    241. .nm_version = 1,
    242. .nm_flags = 0,
    243. .nm_filename = nullptr,
    244. .nm_register_func = Init,
    245. .nm_modname = "entry",
    246. .nm_priv = ((void *)0),
    247. .reserved = {0},
    248. };
    249. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
    250. {
    251. napi_module_register(&demoModule);
    252. }
  2. 在ArkTS中声明接口。

    收起
    自动换行
    深色代码主题
    复制
    1. // Index.d.ts
    2. export const getAddress: () => number;
    3. export const getSetSize: () => number;
    4. export const store: (a: number) => void;
    5. export const erase: (a: number) => void;
    6. export const clear: () => void;
    7. export const setTransferDetached: (b: boolean) => void;
  3. ArkTS对象调用Native侧实现的各项功能。

    在转移模式下,跨线程传递后,原来的ArkTS对象与Native对象解绑,因此不能继续访问。示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. import testNapi from 'libentry.so';
    2. import { taskpool } from '@kit.ArkTS';
    3. @Concurrent
    4. function getAddress() {
    5. let address: number = testNapi.getAddress();
    6. console.info('taskpool:: address is ' + address);
    7. }
    8. @Concurrent
    9. function store(a: number, b: number, c: number) {
    10. let size: number = testNapi.getSetSize();
    11. console.info('set size is ' + size + ' before store');
    12. testNapi.store(a);
    13. testNapi.store(b);
    14. testNapi.store(c);
    15. size = testNapi.getSetSize();
    16. console.info('set size is ' + size + ' after store');
    17. }
    18. @Concurrent
    19. function erase(a: number) {
    20. let size: number = testNapi.getSetSize();
    21. console.info('set size is ' + size + ' before erase');
    22. testNapi.erase(a);
    23. size = testNapi.getSetSize();
    24. console.info('set size is ' + size + ' after erase');
    25. }
    26. @Concurrent
    27. function clear() {
    28. let size: number = testNapi.getSetSize();
    29. console.info('set size is ' + size + ' before clear');
    30. testNapi.clear();
    31. size = testNapi.getSetSize();
    32. console.info('set size is ' + size + ' after clear');
    33. }
    34. // 转移模式
    35. async function test(): Promise<void> {
    36. // setTransferDetached 设置为true,表示传输方式为转移模式
    37. testNapi.setTransferDetached(true);
    38. let address: number = testNapi.getAddress();
    39. console.info('host thread address is ' + address);
    40. // 传递testNapi是为了触发转移,不是供函数使用
    41. let task1 = new taskpool.Task(getAddress, testNapi);
    42. await taskpool.execute(task1);
    43. let task2 = new taskpool.Task(store, 1, 2, 3);
    44. await taskpool.execute(task2);
    45. let task3 = new taskpool.Task(store, 4, 5, 6);
    46. await taskpool.execute(task3);
    47. // 由于已经设置了转移模式,且testNapi已跨线程传递,所以主线程无法继续访问到Native对象的值
    48. let size: number = testNapi.getSetSize();
    49. // 输出的日志为“host thread size is undefined”
    50. console.info('host thread size is ' + size);
    51. let task4 = new taskpool.Task(erase, 3);
    52. await taskpool.execute(task4);
    53. let task5 = new taskpool.Task(erase, 5);
    54. await taskpool.execute(task5);
    55. let task6 = new taskpool.Task(clear);
    56. await taskpool.execute(task6);
    57. }
    58. @Entry
    59. @Component
    60. struct Index {
    61. @State message: string = 'Hello World';
    62. build() {
    63. Row() {
    64. Column() {
    65. Text(this.message)
    66. .fontSize($r('app.float.page_text_font_size'))
    67. .fontWeight(FontWeight.Bold)
    68. .onClick(() => {
    69. test();
    70. })
    71. }
    72. .width('100%')
    73. }
    74. .height('100%')
    75. }
    76. }

    在共享模式下,跨线程传递后,原来的ArkTS对象还可以继续访问Native对象。示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. // Index.ets
    2. import testNapi from 'libentry.so';
    3. import { taskpool } from '@kit.ArkTS';
    4. @Concurrent
    5. function getAddress() {
    6. let address: number = testNapi.getAddress();
    7. console.info('taskpool:: address is ' + address);
    8. }
    9. @Concurrent
    10. function store(a: number, b: number, c: number) {
    11. let size: number = testNapi.getSetSize();
    12. console.info('set size is ' + size + ' before store');
    13. testNapi.store(a);
    14. testNapi.store(b);
    15. testNapi.store(c);
    16. size = testNapi.getSetSize();
    17. console.info('set size is ' + size + ' after store');
    18. }
    19. @Concurrent
    20. function erase(a:number) {
    21. let size: number = testNapi.getSetSize();
    22. console.info('set size is ' + size + ' before erase');
    23. testNapi.erase(a);
    24. size = testNapi.getSetSize();
    25. console.info('set size is ' + size + ' after erase');
    26. }
    27. @Concurrent
    28. function clear() {
    29. let size: number = testNapi.getSetSize();
    30. console.info('set size is ' + size + ' before clear');
    31. testNapi.clear();
    32. size = testNapi.getSetSize();
    33. console.info('set size is ' + size + ' after clear');
    34. }
    35. // 共享模式
    36. async function test(): Promise<void> {
    37. let address: number = testNapi.getAddress();
    38. console.info('host thread address is ' + address);
    39. let task1 = new taskpool.Task(getAddress, testNapi);
    40. await taskpool.execute(task1);
    41. let task2 = new taskpool.Task(store, 1, 2, 3);
    42. await taskpool.execute(task2);
    43. let task3 = new taskpool.Task(store, 4, 5, 6);
    44. await taskpool.execute(task3);
    45. // 由于默认的传输模式为共享模式,testNapi跨线程传递后,主线程可以继续访问Native对象的值
    46. let size: number = testNapi.getSetSize();
    47. // 输出的日志为“host thread size is 6”
    48. console.info('host thread size is ' + size);
    49. let task4 = new taskpool.Task(erase, 3);
    50. await taskpool.execute(task4);
    51. let task5 = new taskpool.Task(erase, 5);
    52. await taskpool.execute(task5);
    53. let task6 = new taskpool.Task(clear);
    54. await taskpool.execute(task6);
    55. }
    56. @Entry
    57. @Component
    58. struct Index {
    59. @State message: string = 'Hello World';
    60. build() {
    61. Row() {
    62. Column() {
    63. Text(this.message)
    64. .fontSize($r('app.float.page_text_font_size'))
    65. .fontWeight(FontWeight.Bold)
    66. .onClick(() => {
    67. test();
    68. })
    69. }
    70. .width('100%')
    71. }
    72. .height('100%')
    73. }
    74. }
在 指南 中进行搜索
请输入您想要搜索的关键词