1# Performing Lifecycle Management Using Node-API 2 3## Introduction 4 5In Node-API, **napi_value** is an abstract data type that represents an ArkTS value of any type, which includes the basic type (such as number, string, or Boolean) and the composite type (such as array, function, or object). 6 7The **napi_value** lifecycle is closely related to the lifecycle of the ArkTS value. When an ArkTS value is garbage-collected, the **napi_value** associated with it is no longer valid. Avoid using the **napi_value** when the ArkTS value no longer exists. 8 9Scope is used to manage the **napi_value** lifecycle in the framework layer. You can use **napi_open_handle_scope** to open a scope and use **napi_close_handle_scope** to close a scope. By creating a **napi_value** in a scope, you can ensure that the **napi_value** is automatically released when the scope ends. This helps prevent memory leaks. 10 11**napi_ref** is a Node-API type used to manage the **napi_value** lifecycle. It allows reference to a **napi_value** during its lifecycle, even if the value is beyond its original context. The reference allows a **napi_value** to be shared in different contexts and released in a timely manner. 12 13## Basic Concepts 14 15Node-API provides APIs for creating and manipulating ArkTS objects, managing references to and lifecycle of the ArkTS objects, and registering garbage collection (GC) callbacks in C/C++. Before you get started, you need to understand the following concepts: 16 17- Scope: used to ensure that the objects created within a certain scope remain active and are properly cleared when no longer required. Node-API provides APIs for creating and closing normal and escapeable scopes. 18- Reference management: Node-API provides APIs for creating, deleting, and managing object references to extend the lifecycle of objects and prevent the use-after-free issues. In addition, reference management also helps prevent memory leaks. 19- Escapeable scope: used to return the values created within the **escapable_handle_scope** to a parent scope. It is created by **napi_open_escapable_handle_scope** and closed by **napi_close_escapable_handle_scope**. 20- GC callback: You can register GC callbacks to perform specific cleanup operations when ArkTS objects are garbage-collected. 21 22Understanding these concepts helps you securely and effectively manipulate ArkTS objects in C/C++ and perform object lifecycle management. 23 24## Available APIs 25 26The following table lists the APIs for ArkTS object lifecycle management. 27| API| Description| 28| -------- | -------- | 29| napi_open_handle_scope | Opens a scope.<br/>When processing ArkTS objects with Node-API, you need to create a temporary scope to store object references so that the objects can be correctly accessed during the execution and closed after the execution. | 30| napi_close_handle_scope | Closes a scope. | 31| napi_open_escapable_handle_scope | Opens a scope from which one object can be prompted to the outer scope. | 32| napi_close_escapable_handle_scope | Closes an escapable handle scope. | 33| napi_escape_handle | Promotes the handle to an ArkTS object so that it is valid for the lifetime of its parent scope.| 34| napi_create_reference | Creates a reference to a value to extend the ArkTS object's lifespan. | 35| napi_delete_reference | Deletes a reference. | 36| napi_reference_ref | Increments the reference count. | 37| napi_reference_unref | Decrements the reference count. | 38| napi_get_reference_value | Obtains the ArkTS object associated with the reference.| 39| napi_add_finalizer | Adds a **napi_finalize** callback, which will be called to clean up or release resources before the ArkTS object is garbage-collected.| 40 41## Example 42 43If you are just starting out with Node-API, see [Node-API Development Process](use-napi-process.md). The following demonstrates only the C++ and ArkTS code related to lifecycle management. 44 45### napi_open_handle_scope and napi_close_handle_scope 46 47Use **napi_open_handle_scope** to open a scope, and use **napi_close_handle_scope** to close it. You can use these two APIs to manage the **napi_value** lifecycle of an ArkTS object, which prevents the object from being incorrectly garbage-collected. 48Properly using these two APIs can minimize lifecycle and prevent memory leaks. 49 50For details about the code, see [Lifecycle Management](napi-guidelines.md). 51 52CPP code: 53 54```cpp 55#include "napi/native_api.h" 56 57static napi_value HandleScopeTest(napi_env env, napi_callback_info info) 58{ 59 // Call napi_open_handle_scope to open a scope. 60 napi_handle_scope scope; 61 napi_open_handle_scope(env, &scope); 62 // Create an object within the scope. 63 napi_value obj = nullptr; 64 napi_create_object(env, &obj); 65 // Add a property to the object. 66 napi_value value = nullptr; 67 napi_create_string_utf8(env, "handleScope", NAPI_AUTO_LENGTH, &value); 68 napi_set_named_property(env, obj, "key", value); 69 // Obtain the object property in the scope and return it. 70 napi_value result = nullptr; 71 napi_get_named_property(env, obj, "key", &result); 72 // Close the scope. Then, the object handle created within the scope is automatically released. 73 napi_close_handle_scope(env, scope); 74 // The value of result is 'handleScope'. 75 return result; 76} 77 78static napi_value HandleScope(napi_env env, napi_callback_info info) 79{ 80 // Call napi_open_handle_scope to open a scope. 81 napi_handle_scope scope; 82 napi_open_handle_scope(env, &scope); 83 // Create an object within the scope. 84 napi_value obj = nullptr; 85 napi_create_object(env, &obj); 86 // Add a property to the object. 87 napi_value value = nullptr; 88 napi_create_string_utf8(env, "handleScope", NAPI_AUTO_LENGTH, &value); 89 napi_set_named_property(env, obj, "key", value); 90 // Close the scope. Then, the object handle created within the scope is automatically released. 91 napi_close_handle_scope(env, scope); 92 // Obtain and return the object property outside the scope. In this example, "undefined" is obtained. 93 napi_value result = nullptr; 94 napi_get_named_property(env, obj, "key", &result); 95 return result; 96} 97``` 98 99API declaration: 100 101```ts 102// index.d.ts 103export const handleScopeTest: () => string; 104export const handleScope: () => string; 105``` 106 107ArkTS code: 108 109```ts 110import hilog from '@ohos.hilog' 111import testNapi from 'libentry.so' 112try { 113 hilog.info(0x0000, 'testTag', 'Test Node-API handleScopeTest: %{public}s', testNapi.handleScopeTest()); 114 hilog.info(0x0000, 'testTag', 'Test Node-API handleScope: %{public}s', testNapi.handleScope()); 115} catch (error) { 116 hilog.error(0x0000, 'testTag', 'Test Node-API handleScopeTest errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message); 117} 118``` 119 120### napi_open_escapable_handle_scope, napi_close_escapable_handle_scope, and napi_escape_handle 121 122Use **napi_open_escapable_handle_scope** to open an escapeable scope, which allows the declared values in the scope to be returned to the parent scope. You can use **napi_close_escapable_handle_scope** to close it. 123 124Use **napi_escape_handle** to promote the lifecycle of an ArkTS object so that it is valid for the lifetime of the parent scope. 125These APIs are helpful for managing ArkTS objects more flexibly in C/C++, especially when passing cross-scope values. 126 127CPP code: 128 129```cpp 130#include "napi/native_api.h" 131 132static napi_value EscapableHandleScopeTest(napi_env env, napi_callback_info info) 133{ 134 // Create an escapeable scope. 135 napi_escapable_handle_scope scope; 136 napi_open_escapable_handle_scope(env, &scope); 137 // Create an object within the escapeable scope. 138 napi_value obj = nullptr; 139 napi_create_object(env, &obj); 140 // Add a property to the object. 141 napi_value value = nullptr; 142 napi_create_string_utf8(env, "Test napi_escapable_handle_scope", NAPI_AUTO_LENGTH, &value); 143 napi_set_named_property(env, obj, "key", value); 144 // Call napi_escape_handle to promote the ArkTS object handle to make it valid with the lifetime of the outer scope. 145 napi_value escapedObj = nullptr; 146 napi_escape_handle(env, scope, obj, &escapedObj); 147 // Close the escapeable scope to clear resources. 148 napi_close_escapable_handle_scope(env, scope); 149 // Obtain and return the property of the object whose scope is promoted. You can also obtain napi_escapable_handle_scope here. 150 napi_value result = nullptr; 151 // To verify the escape implementation, obtain the object property here. "undefined" is obtained. 152 napi_get_named_property(env, escapedObj, "key", &result); 153 return result; 154} 155``` 156 157API declaration: 158 159```ts 160// index.d.ts 161export const escapableHandleScopeTest: () => string; 162``` 163 164ArkTS code: 165 166```ts 167import hilog from '@ohos.hilog' 168import testNapi from 'libentry.so' 169try { 170 hilog.info(0x0000, 'testTag', 'Test Node-API EscapableHandleScopeTest: %{public}s', testNapi.escapableHandleScopeTest()); 171} catch (error) { 172 hilog.error(0x0000, 'testTag', 'Test Node-API EscapableHandleScopeTest errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message); 173} 174``` 175 176### napi_create_reference and napi_delete_reference 177 178Use **napi_create_reference** to create a reference for an object to extend its lifespan. The caller needs to manage the reference lifespan. Use **napi_delete_reference** to delete a reference. 179 180### napi_reference_ref and napi_reference_unref 181 182Use **napi_reference_ref** to increment the reference count and use **napi_reference_unref** to decrement the reference count, and return the new count value. 183 184### napi_get_reference_value 185 186Use **napi_get_reference_value** to obtain the ArkTS object associated with the reference. 187 188### napi_add_finalizer 189 190Use **napi_add_finalizer** to add a **napi_finalizer** callback, which will be called when the ArkTS object is garbage-collected. 191 192CPP code: 193 194```cpp 195// log.h is used to print logs in C++. 196#include "hilog/log.h" 197#include "napi/native_api.h" 198// Create a pointer to napi_ref to store the created reference. Before calling napi_create_reference, you need to allocate a variable of the napi_ref type and pass its address to the parameter in result. 199napi_ref g_ref; 200 201void Finalizer(napi_env env, void *data, void *hint) 202{ 203 // Clear resources. 204 OH_LOG_INFO(LOG_APP, "Node-API: Use terminators to release resources."); 205} 206 207static napi_value CreateReference(napi_env env, napi_callback_info info) 208{ 209 napi_value obj = nullptr; 210 napi_create_object(env, &obj); 211 napi_value value = nullptr; 212 napi_create_string_utf8(env, "CreateReference", NAPI_AUTO_LENGTH, &value); 213 // Add a property to the object. 214 napi_set_named_property(env, obj, "key", value); 215 // Create a reference to the ArkTS object. 216 napi_status status = napi_create_reference(env, obj, 1, &g_ref); 217 if (status != napi_ok) { 218 napi_throw_error(env, nullptr, "napi_create_reference fail"); 219 return nullptr; 220 } 221 // Add a terminator. 222 void *data = {}; 223 napi_add_finalizer(env, obj, data, Finalizer, nullptr, &g_ref); 224 // Increment the reference count and return the new reference count. 225 uint32_t result = 0; 226 napi_reference_ref(env, g_ref, &result); 227 OH_LOG_INFO(LOG_APP, "napi_reference_ref, count = %{public}d.", result); 228 if (result != 2) { 229 // If the reference count passed in does not increase, throw an error. 230 napi_throw_error(env, nullptr, "napi_reference_ref fail"); 231 return nullptr; 232 } 233 return obj; 234} 235 236static napi_value UseReference(napi_env env, napi_callback_info info) 237{ 238 napi_value obj = nullptr; 239 // Call napi_get_reference_value to obtain the referenced ArkTS object. 240 napi_status status = napi_get_reference_value(env, g_ref, &obj); 241 if (status != napi_ok) { 242 napi_throw_error(env, nullptr, "napi_get_reference_value fail"); 243 return nullptr; 244 } 245 // Return the obtained object. 246 return obj; 247} 248 249static napi_value DeleteReference(napi_env env, napi_callback_info info) 250{ 251 // Decrement the reference count and return the new reference count. 252 uint32_t result = 0; 253 napi_value count = nullptr; 254 napi_reference_unref(env, g_ref, &result); 255 OH_LOG_INFO(LOG_APP, "napi_reference_ref, count = %{public}d.", result); 256 if (result != 1) { 257 // If the reference count passed in does not decrease, throw an error. 258 napi_throw_error(env, nullptr, "napi_reference_unref fail"); 259 return nullptr; 260 } 261 // Call napi_delete_reference to delete the reference to the ArkTS object. 262 napi_status status = napi_delete_reference(env, g_ref); 263 if (status != napi_ok) { 264 napi_throw_error(env, nullptr, "napi_delete_reference fail"); 265 return nullptr; 266 } 267 napi_value returnResult = nullptr; 268 napi_create_string_utf8(env, "napi_delete_reference success", NAPI_AUTO_LENGTH, &returnResult); 269 return returnResult; 270} 271``` 272 273API declaration: 274 275```ts 276// index.d.ts 277export const createReference: () => Object | void; 278export const useReference: () => Object | void; 279export const deleteReference: () => string | void; 280``` 281 282ArkTS code: 283 284```ts 285import hilog from '@ohos.hilog' 286import testNapi from 'libentry.so' 287try { 288 hilog.info(0x0000, 'testTag', 'Test Node-API createReference: %{public}s', JSON.stringify(testNapi.createReference())); 289 hilog.info(0x0000, 'testTag', 'Test Node-API useReference: %{public}s', JSON.stringify(testNapi.useReference())); 290 hilog.info(0x0000, 'testTag', 'Test Node-API deleteReference: %{public}s', testNapi.deleteReference()); 291} catch (error) { 292 hilog.error(0x0000, 'testTag', 'Test Node-API ReferenceTest errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message); 293} 294``` 295 296To print logs in the native CPP, add the following information to the **CMakeLists.txt** file and add the header file by using **#include "hilog/log.h"**. 297 298```text 299// CMakeLists.txt 300add_definitions( "-DLOG_DOMAIN=0xd0d0" ) 301add_definitions( "-DLOG_TAG=\"testTag\"" ) 302target_link_libraries(entry PUBLIC libhilog_ndk.z.so) 303``` 304