智能客服
你问我答,随时在线为你解决问题
在ArkTS应用开发中,有很多场景需要将ArkTS对象与Native对象进行绑定。ArkTS对象将数据写入Native对象,Native对象再将数据写入目的地。例如,将ArkTS对象中的数据写入C++数据库场景。
Native Transferable对象有两种模式:共享模式和转移模式。本示例将详细说明如何实现这两种模式。
Native实现各项功能。
- // napi_init.cpp
- #include <mutex>
- #include <unordered_set>
- #include "napi/native_api.h"
- #include <hilog/log.h>
-
- class CustomNativeObject {
- public:
- CustomNativeObject() {}
- ~CustomNativeObject() = default;
- static CustomNativeObject& GetInstance()
- {
- static CustomNativeObject instance;
- return instance;
- }
-
- static napi_value GetAddress(napi_env env, napi_callback_info info)
- {
- napi_value thisVar = nullptr;
- napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
- if (thisVar == nullptr) {
- return nullptr;
- }
- void *object = nullptr;
- napi_unwrap(env, thisVar, &object);
- if (object == nullptr) {
- return nullptr;
- }
-
- uint64_t addressVal = reinterpret_cast<uint64_t>(object);
- napi_value address = nullptr;
- napi_create_bigint_uint64(env, addressVal, &address);
- return address;
- }
-
- // 获取数组大小
- static napi_value GetSetSize(napi_env env, napi_callback_info info)
- {
- napi_value thisVar = nullptr;
- napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
- if (thisVar == nullptr) {
- return nullptr;
- }
- void *object = nullptr;
- napi_unwrap(env, thisVar, &object);
- if (object == nullptr) {
- return nullptr;
- }
- CustomNativeObject *obj = static_cast<CustomNativeObject*>(object);
- std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
- uint32_t setSize = reinterpret_cast<CustomNativeObject *>(object)->numberSet_.size();
- napi_value napiSize = nullptr;
- napi_create_uint32(env, setSize, &napiSize);
- return napiSize;
- }
-
- // 往数组里插入元素
- static napi_value Store(napi_env env, napi_callback_info info)
- {
- size_t argc = 1;
- napi_value args[1] = {nullptr};
- napi_value thisVar = nullptr;
- napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
- if (argc != 1) {
- napi_throw_error(env, nullptr, "Store args number must be one.");
- return nullptr;
- }
- napi_valuetype type = napi_undefined;
- napi_typeof(env, args[0], &type);
- if (type != napi_number) {
- napi_throw_error(env, nullptr, "Store args is not number.");
- return nullptr;
- }
- if (thisVar == nullptr) {
- return nullptr;
- }
-
- void *object = nullptr;
- napi_unwrap(env, thisVar, &object);
- if (object == nullptr) {
- return nullptr;
- }
-
- uint32_t value = 0;
- napi_get_value_uint32(env, args[0], &value);
- CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
- std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
- reinterpret_cast<CustomNativeObject *>(object)->numberSet_.insert(value);
- return nullptr;
- }
-
- // 删除数组元素
- static napi_value Erase(napi_env env, napi_callback_info info)
- {
- size_t argc = 1;
- napi_value args[1] = {nullptr};
- napi_value thisVar = nullptr;
- napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
- if (argc != 1) {
- napi_throw_error(env, nullptr, "Erase args number must be one.");
- return nullptr;
- }
- napi_valuetype type = napi_undefined;
- napi_typeof(env, args[0], &type);
- if (type != napi_number) {
- napi_throw_error(env, nullptr, "Erase args is not number.");
- return nullptr;
- }
- if (thisVar == nullptr) {
- return nullptr;
- }
-
- void *object = nullptr;
- napi_unwrap(env, thisVar, &object);
- if (object == nullptr) {
- return nullptr;
- }
-
- uint32_t value = 0;
- napi_get_value_uint32(env, args[0], &value);
-
- CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
- std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
- reinterpret_cast<CustomNativeObject *>(object)->numberSet_.erase(value);
- return nullptr;
- }
-
- // 清空数组
- static napi_value Clear(napi_env env, napi_callback_info info)
- {
- napi_value thisVar = nullptr;
- napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
- if (thisVar == nullptr) {
- return nullptr;
- }
- void *object = nullptr;
- napi_unwrap(env, thisVar, &object);
- if (object == nullptr) {
- return nullptr;
- }
- CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
- std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
- reinterpret_cast<CustomNativeObject *>(object)->numberSet_.clear();
- return nullptr;
- }
-
- // 设置传输模式
- static napi_value SetTransferDetached(napi_env env, napi_callback_info info)
- {
- size_t argc = 1;
- napi_value args[1];
- napi_value thisVar;
- napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr);
- if (argc != 1) {
- napi_throw_error(env, nullptr, "SetTransferDetached args number must be one.");
- return nullptr;
- }
-
- if (thisVar == nullptr) {
- return nullptr;
- }
-
- napi_valuetype type = napi_undefined;
- napi_typeof(env, args[0], &type);
- if (type != napi_boolean) {
- napi_throw_error(env, nullptr, "SetTransferDetached args is not boolean.");
- return nullptr;
- }
-
- bool isDetached;
- napi_get_value_bool(env, args[0], &isDetached);
-
- void *object = nullptr;
- napi_unwrap(env, thisVar, &object);
- if (object == nullptr) {
- return nullptr;
- }
- CustomNativeObject *obj = static_cast<CustomNativeObject *>(object);
- std::lock_guard<std::mutex> lock(obj->numberSetMutex_);
- obj->isDetached_ = isDetached;
- return nullptr;
- }
-
- bool isDetached_ = false;
- std::mutex numberSetMutex_{};
-
- private:
- CustomNativeObject(const CustomNativeObject &) = delete;
- CustomNativeObject &operator=(const CustomNativeObject &) = delete;
-
- std::unordered_set<uint32_t> numberSet_{};
- };
-
- void FinalizeCallback(napi_env env, void *data, void *hint)
- {
- return;
- }
-
- // 解绑回调,在序列化时调用,可在对象解绑时执行一些清理操作
- void* DetachCallback(napi_env env, void *value, void *hint)
- {
- if (hint == nullptr) {
- return value;
- }
- CustomNativeObject *obj = static_cast<CustomNativeObject *>(value);
- // 加锁保护 isDetached_ 的读取,与 SetTransferDetached 的写入互斥
- bool isDetached = false;
- {
- std::lock_guard<std::mutex> lock(obj->numberSetMutex_); // 需要std::mutex numberSetMutex_{};改为public
- isDetached = obj->isDetached_;
- }
- if (isDetached) {
- napi_value jsObject = nullptr;
- napi_get_reference_value(env, reinterpret_cast<napi_ref>(hint), &jsObject);
- void *object = nullptr;
- napi_remove_wrap(env, jsObject, &object);
- }
- return value;
- }
-
- // 绑定回调,在反序列化时调用
- napi_value AttachCallback(napi_env env, void* value, void* hint)
- {
- napi_value object = nullptr;
- napi_create_object(env, &object);
- napi_property_descriptor desc[] = {
- {"getAddress", nullptr, CustomNativeObject::GetAddress, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"getSetSize", nullptr, CustomNativeObject::GetSetSize, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"store", nullptr, CustomNativeObject::Store, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"erase", nullptr, CustomNativeObject::Erase, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"clear", nullptr, CustomNativeObject::Clear, nullptr, nullptr, nullptr, napi_default, nullptr}};
- napi_define_properties(env, object, sizeof(desc) / sizeof(desc[0]), desc);
- // 将JS对象object和native对象value生命周期进行绑定
- napi_wrap(env, object, value, FinalizeCallback, nullptr, nullptr);
-
- // 创建指向新 JS 对象的强引用
- napi_ref objectRef = nullptr;
- napi_create_reference(env, object, 1, &objectRef);
-
- // JS对象携带native信息
- napi_coerce_to_native_binding_object(env, object, DetachCallback, AttachCallback, value, objectRef);
- return object;
- }
-
- EXTERN_C_START
- static napi_value Init(napi_env env, napi_value exports)
- {
- napi_property_descriptor desc[] = {
- {"getAddress", nullptr, CustomNativeObject::GetAddress, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"getSetSize", nullptr, CustomNativeObject::GetSetSize, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"store", nullptr, CustomNativeObject::Store, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"erase", nullptr, CustomNativeObject::Erase, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"clear", nullptr, CustomNativeObject::Clear, nullptr, nullptr, nullptr, napi_default, nullptr},
- {"setTransferDetached", nullptr, CustomNativeObject::SetTransferDetached,
- nullptr, nullptr, nullptr, napi_default, nullptr}};
- napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
- auto &object = CustomNativeObject::GetInstance();
- napi_wrap(env, exports, reinterpret_cast<void *>(&object), FinalizeCallback, nullptr, nullptr);
- napi_ref exportsRef;
- napi_create_reference(env, exports, 1, &exportsRef);
- napi_coerce_to_native_binding_object(env, exports, DetachCallback,
- AttachCallback, reinterpret_cast<void *>(&object), exportsRef);
- 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);
- }
在ArkTS中声明接口。
- // Index.d.ts
- export const getAddress: () => number;
-
- export const getSetSize: () => number;
-
- export const store: (a: number) => void;
-
- export const erase: (a: number) => void;
-
- export const clear: () => void;
-
- export const setTransferDetached: (b: boolean) => void;
ArkTS对象调用Native侧实现的各项功能。
在转移模式下,跨线程传递后,原来的ArkTS对象与Native对象解绑,因此不能继续访问。示例如下:
- import testNapi from 'libentry.so';
- import { taskpool } from '@kit.ArkTS';
-
- @Concurrent
- function getAddress() {
- let address: number = testNapi.getAddress();
- console.info('taskpool:: address is ' + address);
- }
-
- @Concurrent
- function store(a: number, b: number, c: number) {
- let size: number = testNapi.getSetSize();
- console.info('set size is ' + size + ' before store');
- testNapi.store(a);
- testNapi.store(b);
- testNapi.store(c);
- size = testNapi.getSetSize();
- console.info('set size is ' + size + ' after store');
- }
-
- @Concurrent
- function erase(a: number) {
- let size: number = testNapi.getSetSize();
- console.info('set size is ' + size + ' before erase');
- testNapi.erase(a);
- size = testNapi.getSetSize();
- console.info('set size is ' + size + ' after erase');
- }
-
- @Concurrent
- function clear() {
- let size: number = testNapi.getSetSize();
- console.info('set size is ' + size + ' before clear');
- testNapi.clear();
- size = testNapi.getSetSize();
- console.info('set size is ' + size + ' after clear');
- }
-
- // 转移模式
- async function test(): Promise<void> {
- // setTransferDetached 设置为true,表示传输方式为转移模式
- testNapi.setTransferDetached(true);
- let address: number = testNapi.getAddress();
- console.info('host thread address is ' + address);
- // 传递testNapi是为了触发转移,不是供函数使用
- let task1 = new taskpool.Task(getAddress, testNapi);
- await taskpool.execute(task1);
-
- let task2 = new taskpool.Task(store, 1, 2, 3);
- await taskpool.execute(task2);
-
- let task3 = new taskpool.Task(store, 4, 5, 6);
- await taskpool.execute(task3);
-
- // 由于已经设置了转移模式,且testNapi已跨线程传递,所以主线程无法继续访问到Native对象的值
- let size: number = testNapi.getSetSize();
- // 输出的日志为“host thread size is undefined”
- console.info('host thread size is ' + size);
-
- let task4 = new taskpool.Task(erase, 3);
- await taskpool.execute(task4);
-
- let task5 = new taskpool.Task(erase, 5);
- await taskpool.execute(task5);
-
- let task6 = new taskpool.Task(clear);
- await taskpool.execute(task6);
- }
-
- @Entry
- @Component
- struct Index {
- @State message: string = 'Hello World';
-
- build() {
- Row() {
- Column() {
- Text(this.message)
- .fontSize($r('app.float.page_text_font_size'))
- .fontWeight(FontWeight.Bold)
- .onClick(() => {
- test();
- })
- }
- .width('100%')
- }
- .height('100%')
- }
- }
在共享模式下,跨线程传递后,原来的ArkTS对象还可以继续访问Native对象。示例如下:
- // Index.ets
- import testNapi from 'libentry.so';
- import { taskpool } from '@kit.ArkTS';
-
- @Concurrent
- function getAddress() {
- let address: number = testNapi.getAddress();
- console.info('taskpool:: address is ' + address);
- }
-
- @Concurrent
- function store(a: number, b: number, c: number) {
- let size: number = testNapi.getSetSize();
- console.info('set size is ' + size + ' before store');
- testNapi.store(a);
- testNapi.store(b);
- testNapi.store(c);
- size = testNapi.getSetSize();
- console.info('set size is ' + size + ' after store');
- }
-
- @Concurrent
- function erase(a:number) {
- let size: number = testNapi.getSetSize();
- console.info('set size is ' + size + ' before erase');
- testNapi.erase(a);
- size = testNapi.getSetSize();
- console.info('set size is ' + size + ' after erase');
- }
-
- @Concurrent
- function clear() {
- let size: number = testNapi.getSetSize();
- console.info('set size is ' + size + ' before clear');
- testNapi.clear();
- size = testNapi.getSetSize();
- console.info('set size is ' + size + ' after clear');
- }
-
- // 共享模式
- async function test(): Promise<void> {
- let address: number = testNapi.getAddress();
- console.info('host thread address is ' + address);
-
- let task1 = new taskpool.Task(getAddress, testNapi);
- await taskpool.execute(task1);
-
- let task2 = new taskpool.Task(store, 1, 2, 3);
- await taskpool.execute(task2);
-
- let task3 = new taskpool.Task(store, 4, 5, 6);
- await taskpool.execute(task3);
-
- // 由于默认的传输模式为共享模式,testNapi跨线程传递后,主线程可以继续访问Native对象的值
- let size: number = testNapi.getSetSize();
- // 输出的日志为“host thread size is 6”
- console.info('host thread size is ' + size);
-
- let task4 = new taskpool.Task(erase, 3);
- await taskpool.execute(task4);
-
- let task5 = new taskpool.Task(erase, 5);
- await taskpool.execute(task5);
-
- let task6 = new taskpool.Task(clear);
- await taskpool.execute(task6);
- }
-
- @Entry
- @Component
- struct Index {
- @State message: string = 'Hello World';
-
- build() {
- Row() {
- Column() {
- Text(this.message)
- .fontSize($r('app.float.page_text_font_size'))
- .fontWeight(FontWeight.Bold)
- .onClick(() => {
- test();
- })
- }
- .width('100%')
- }
- .height('100%')
- }
- }