1# Native与ArkTS对象绑定
2
3## 场景介绍
4
5通过`napi_wrap`将ArkTS对象与Native的C++对象绑定,后续操作时再通过`napi_unwrap`将ArkTS对象绑定的C++对象取出,并对其进行操作。
6
7## 使用示例
8
91. 接口声明、编译配置以及模块注册
10
11   **接口声明**
12
13   ```ts
14   // index.d.ts
15   export class MyObject {
16      constructor(arg: number);
17      plusOne: () => number;
18
19      public get value();
20      public set value(newVal: number);
21   }
22   ```
23
24   **编译配置**
25
26   ```
27   # the minimum version of CMake.
28   cmake_minimum_required(VERSION 3.5.0)
29   project(napi_wrap_demo)
30
31   set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
32
33   if(DEFINED PACKAGE_FIND_FILE)
34       include(${PACKAGE_FIND_FILE})
35   endif()
36
37   include_directories(${NATIVERENDER_ROOT_PATH}
38                       ${NATIVERENDER_ROOT_PATH}/include)
39
40   add_definitions("-DLOG_DOMAIN=0x0000")
41   add_definitions("-DLOG_TAG=\"testTag\"")
42
43   add_library(entry SHARED napi_init.cpp)
44   target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so)
45   ```
46
47   **模块注册**
48
49   ```cpp
50   // napi_init.cpp
51   #include "napi/native_api.h"
52   #include "hilog/log.h"
53
54   class MyObject {
55    public:
56     static napi_value Init(napi_env env, napi_value exports);
57     static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
58
59    private:
60     explicit MyObject(double value_ = 0);
61     ~MyObject();
62
63     static napi_value New(napi_env env, napi_callback_info info);
64     static napi_value GetValue(napi_env env, napi_callback_info info);
65     static napi_value SetValue(napi_env env, napi_callback_info info);
66     static napi_value PlusOne(napi_env env, napi_callback_info info);
67
68     double value_;
69     napi_env env_;
70     napi_ref wrapper_;
71   };
72
73   static thread_local napi_ref g_ref = nullptr;
74
75   MyObject::MyObject(double value)
76       : value_(value), env_(nullptr), wrapper_(nullptr) {}
77
78   MyObject::~MyObject()
79   {
80     napi_delete_reference(env_, wrapper_);
81   }
82
83   void MyObject::Destructor(napi_env env,
84                             void* nativeObject,
85                             [[maybe_unused]] void* finalize_hint)
86   {
87     OH_LOG_INFO(LOG_APP, "MyObject::Destructor called");
88     delete reinterpret_cast<MyObject*>(nativeObject);
89   }
90
91   napi_value MyObject::Init(napi_env env, napi_value exports)
92   {
93     napi_property_descriptor properties[] = {
94         { "value", 0, 0, GetValue, SetValue, 0, napi_default, 0 },
95         { "plusOne", nullptr, PlusOne, nullptr, nullptr, nullptr, napi_default, nullptr }
96     };
97
98     napi_value cons;
99     napi_define_class(env, "MyObject", NAPI_AUTO_LENGTH, New, nullptr, 2,
100                              properties, &cons);
101
102     napi_create_reference(env, cons, 1, &g_ref);
103     napi_set_named_property(env, exports, "MyObject", cons);
104     return exports;
105   }
106
107   EXTERN_C_START
108   static napi_value Init(napi_env env, napi_value exports)
109   {
110       MyObject::Init(env, exports);
111       return exports;
112   }
113   EXTERN_C_END
114
115   static napi_module nativeModule = {
116       .nm_version = 1,
117       .nm_flags = 0,
118       .nm_filename = nullptr,
119       .nm_register_func = Init,
120       .nm_modname = "entry",
121       .nm_priv = nullptr,
122       .reserved = { 0 },
123   };
124
125   extern "C" __attribute__((constructor)) void RegisterObjectWrapModule()
126   {
127       napi_module_register(&nativeModule);
128   }
129   ```
130
1312. 在构造函数中绑定ArkTS与C++对象
132
133   ```cpp
134   napi_value MyObject::New(napi_env env, napi_callback_info info)
135   {
136     OH_LOG_INFO(LOG_APP, "MyObject::New called");
137
138     napi_value newTarget;
139     napi_get_new_target(env, info, &newTarget);
140     if (newTarget != nullptr) {
141       // 使用`new MyObject(...)`调用方式
142       size_t argc = 1;
143       napi_value args[1];
144       napi_value jsThis;
145       napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
146
147       double value = 0.0;
148       napi_valuetype valuetype;
149       napi_typeof(env, args[0], &valuetype);
150       if (valuetype != napi_undefined) {
151         napi_get_value_double(env, args[0], &value);
152       }
153
154       MyObject* obj = new MyObject(value);
155
156       obj->env_ = env;
157       // 通过napi_wrap将ArkTS对象jsThis与C++对象obj绑定
158       napi_status status = napi_wrap(env,
159                                      jsThis,
160                                      reinterpret_cast<void*>(obj),
161                                      MyObject::Destructor,
162                                      nullptr,  // finalize_hint
163                                      &obj->wrapper_);
164       // napi_wrap失败时,必须手动释放已分配的内存,以防止内存泄漏
165       if (status != napi_ok) {
166         OH_LOG_INFO(LOG_APP, "Failed to bind native object to js object"
167                     ", return code: %{public}d", status);
168         delete obj;
169         return jsThis;
170       }
171       // 从napi_wrap接口的result获取napi_ref的行为,将会为jsThis创建强引用,
172       // 若开发者不需要主动管理jsThis的生命周期,可直接在napi_wrap最后一个参数中传入nullptr,
173       // 或者使用napi_reference_unref方法将napi_ref转为弱引用。
174       uint32_t refCount = 0;
175       napi_reference_unref(env, obj->wrapper_, &refCount);
176
177       return jsThis;
178     } else {
179       // 使用`MyObject(...)`调用方式
180       size_t argc = 1;
181       napi_value args[1];
182       napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
183
184       napi_value cons;
185       napi_get_reference_value(env, g_ref, &cons);
186       napi_value instance;
187       napi_new_instance(env, cons, argc, args, &instance);
188
189       return instance;
190     }
191   }
192   ```
193
1943. 将ArkTS对象之前绑定的C++对象取出,并对其进行操作
195
196   ```cpp
197   napi_value MyObject::GetValue(napi_env env, napi_callback_info info)
198   {
199     OH_LOG_INFO(LOG_APP, "MyObject::GetValue called");
200
201     napi_value jsThis;
202     napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);
203
204     MyObject* obj;
205     // 通过napi_unwrap将jsThis之前绑定的C++对象取出,并对其进行操作
206     napi_unwrap(env, jsThis, reinterpret_cast<void**>(&obj));
207     napi_value num;
208     napi_create_double(env, obj->value_, &num);
209
210     return num;
211   }
212
213   napi_value MyObject::SetValue(napi_env env, napi_callback_info info)
214   {
215     OH_LOG_INFO(LOG_APP, "MyObject::SetValue called");
216
217     size_t argc = 1;
218     napi_value value;
219     napi_value jsThis;
220
221     napi_get_cb_info(env, info, &argc, &value, &jsThis, nullptr);
222
223     MyObject* obj;
224     // 通过napi_unwrap将jsThis之前绑定的C++对象取出,并对其进行操作
225     napi_unwrap(env, jsThis, reinterpret_cast<void**>(&obj));
226     napi_get_value_double(env, value, &obj->value_);
227
228     return nullptr;
229   }
230
231   napi_value MyObject::PlusOne(napi_env env, napi_callback_info info)
232   {
233     OH_LOG_INFO(LOG_APP, "MyObject::PlusOne called");
234
235     napi_value jsThis;
236     napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);
237
238     MyObject* obj;
239     // 通过napi_unwrap将jsThis之前绑定的C++对象取出,并对其进行操作
240     napi_unwrap(env, jsThis, reinterpret_cast<void**>(&obj));
241     obj->value_ += 1;
242     napi_value num;
243     napi_create_double(env, obj->value_, &num);
244
245     return num;
246   }
247   ```
248
2494. ArkTS侧示例代码
250
251   ```ts
252   import hilog from '@ohos.hilog';
253   import { MyObject } from 'libentry.so';
254
255   let object : MyObject = new MyObject(0);
256   object.value = 1023;
257   hilog.info(0x0000, 'testTag', 'MyObject value after set: %{public}d', object.value);
258   hilog.info(0x0000, 'testTag', 'MyObject plusOne: %{public}d', object.plusOne());
259   ```
260