1# 使用JSVM-API接口进行生命周期相关开发 2<!--Kit: NDK Development--> 3<!--Subsystem: arkcompiler--> 4<!--Owner: @yuanxiaogou; @string_sz--> 5<!--Designer: @knightaoko--> 6<!--Tester: @test_lzz--> 7<!--Adviser: @fang-jinxu--> 8 9## 简介 10 11在JSVM-API中,JSVM_Value是一个表示JavaScript值的抽象类型,它可以表示任何JavaScript值,包括基本类型(如数字、字符串、布尔值)和对象类型(如数组、函数、对象等)。 12JSVM_Value的生命周期与JavaScript值的生命周期相关。JavaScript值被垃圾回收后,JSVM_Value将不再有效。避免在JavaScript值不存在时使用JSVM_Value。 13 14框架层的scope通常用于管理JSVM_Value的生命周期。在JSVM-API中,可以使用OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope函数来创建和销毁scope。通过在scope内创建JSVM_Value,可以确保在scope结束时自动释放JSVM_Value,避免内存泄漏。 15 16JSVM_Ref是一个JSVM-API类型,用于管理JSVM_Value的生命周期。JSVM_Ref允许您在JSVM_Value的生命周期内保持对其的引用,即使它已经超出了其原始上下文的范围。这使得您可以在不同的上下文中共享JSVM_Value,并确保在不再需要时正确释放其内存。 17 18合理使用OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope管理JSVM_Value的生命周期,避免发生内存泄漏问题。 19 20每个JSVM_Value属于特定的HandleScope,HandleScope通过OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope来建立和关闭,HandleScope关闭后,所属的JSVM_Value就会自动释放。 21 22## 基本概念 23 24JSVM-API提供了一组功能,使开发人员能够在JSVM-API模块中创建和操作JavaScript对象,管理引用和生命周期,并注册垃圾回收回调函数等。下面是一些基本概念: 25 26- **作用域**:用于创建一个范围,在范围内声明的引用在范围外部将不再生效。JSVM-API提供了创建、关闭普通和可逃逸的作用域的函数。 27- **引用管理**:JSVM-API提供函数来创建、删除和管理对象的引用,以延长对象的生命周期,并避免在使用对象时发生内存泄漏。 28- **可逃逸的作用域**:允许在创建的作用域中声明的对象返回到父作用域,通过OH_JSVM_OpenEscapableHandleScope和OH_JSVM_CloseEscapableHandleScope进行管理。 29- **垃圾回收回调**:允许注册回调函数,以便在JavaScript对象被垃圾回收时执行特定的清理操作。 30 31这些基本概念使开发人员安全且有效地操作JavaScript对象,并确保正确管理对象的生命周期。 32 33## 接口说明 34 35| 接口 | 功能说明 | 36|----------------------------|--------------------------------| 37| OH_JSVM_OpenHandleScope | 打开一个Handle scope,确保scope范围内的JSVM_Value不被GC回收。 | 38| OH_JSVM_CloseHandleScope | 关闭Handle scope。| 39| OH_JSVM_OpenEscapableHandleScope | 打开一个新的scope逃逸Handle scope,在关闭该scope之前创建的对象与父作用域有相同的生命周期。 | 40| OH_JSVM_CloseEscapableHandleScope | 关闭一个scope,在此scope范围外创建的对象不受父作用域保护。 | 41| OH_JSVM_EscapeHandle | 将JavaScript对象的句柄提升到外部作用域,确保在外部作用域中可以持续地使用该对象。 | 42| OH_JSVM_CreateReference | 以指定的引用计数为JavaScript对象创建一个新的引用,该引用将指向传入的对象,引用允许在不同的上下文中使用和共享对象,并且可以有效地跟踪对象的生命周期。 | 43| OH_JSVM_DeleteReference | 释放由OH_JSVM_CreateReference创建的引用,确保对象在不再被使用时能够被正确地释放和回收,避免内存泄漏。 | 44| OH_JSVM_ReferenceRef | 增加由OH_JSVM_CreateReference创建的引用的引用计数,以确保对象在有引用时不会被提前释放。 | 45| OH_JSVM_ReferenceUnref | 减少引用计数,用于管理引用计数。| 46| OH_JSVM_GetReferenceValue | 减少由OH_JSVM_CreateReference创建的引用的引用计数,以确保没有任何引用指向该对象时能正确地释放和回收。 | 47| OH_JSVM_AddFinalizer | 为对象添加JSVM_Finalize回调,以便在JavaScript对象被垃圾回收时调用来释放原生对象。| 48 49## 使用示例 50 51JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅展示接口对应的C++代码。 52 53### OH_JSVM_OpenHandleScope、OH_JSVM_CloseHandleScope 54 55通过接口OH_JSVM_OpenHandleScope创建上下文环境,并使用OH_JSVM_CloseHandleScope关闭。这用于管理JavaScript对象的生命周期,确保在JSVM-API模块中正确处理JavaScript对象句柄,避免垃圾回收问题。 56 57cpp 部分代码: 58 59```cpp 60// OH_JSVM_OpenHandleScope、OH_JSVM_CloseHandleScope的三种样例方法 61static JSVM_Value HandleScopeFor(JSVM_Env env, JSVM_CallbackInfo info) { 62 // 在for循环中频繁调用JSVM接口创建js对象时,要加handle_scope及时释放不再使用的资源。 63 // 下面例子中,每次循环结束局部变量res的生命周期已结束,因此加scope及时释放其持有的js对象,防止内存泄漏 64 constexpr uint32_t DIFF_VALUE_TEN_THOUSAND = 10000; 65 JSVM_Value checked = nullptr; 66 for (int i = 0; i < DIFF_VALUE_TEN_THOUSAND; i++) { 67 JSVM_HandleScope scope = nullptr; 68 JSVM_Status status = OH_JSVM_OpenHandleScope(env, &scope); 69 if (status != JSVM_OK || scope == nullptr) { 70 OH_JSVM_GetBoolean(env, false, &checked); 71 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_OpenHandleScope: failed"); 72 return checked; 73 } 74 JSVM_Value res = nullptr; 75 OH_JSVM_CreateObject(env, &res); 76 status = OH_JSVM_CloseHandleScope(env, scope); 77 if (status != JSVM_OK) { 78 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_CloseHandleScope: failed"); 79 } 80 } 81 OH_JSVM_GetBoolean(env, true, &checked); 82 OH_LOG_INFO(LOG_APP, "JSVM HandleScopeFor: success"); 83 return checked; 84} 85 86// HandleScopeFor注册回调 87static JSVM_CallbackStruct param[] = { 88 {.callback = HandleScopeFor, .data = nullptr}, 89}; 90 91static JSVM_CallbackStruct *method = param; 92// HandleScopeFor方法别名,供JS调用 93static JSVM_PropertyDescriptor descriptor[] = { 94 {"HandleScopeFor", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 95}; 96 97const char *srcCallNative = "HandleScopeFor()"; 98``` 99<!-- @[oh_jsvm_open_handle_scope_and_oh_jsvm_close_handle_scope](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmUsageGuide/JsvmLifeCycle/openhandlescope/src/main/cpp/hello.cpp) --> 100 101预期输出 102``` 103JSVM HandleScopeFor: success 104``` 105 106### OH_JSVM_OpenEscapableHandleScope、OH_JSVM_CloseEscapableHandleScope、OH_JSVM_EscapeHandle 107 108通过接口 OH_JSVM_OpenEscapableHandleScope 创建出一个可逃逸的 handle scope,可将 1 个范围内声明的值返回到父作用域。创建的 scope 需使用 OH_JSVM_CloseEscapableHandleScope 进行关闭。OH_JSVM_EscapeHandle 将传入的 JavaScript 对象的生命周期提升到其父作用域。 109通过上述接口可以更灵活的使用管理传入的 JavaScript 对象,特别是在处理跨作用域的值传递时非常有用。 110 111cpp 部分代码: 112 113```cpp 114// OH_JSVM_OpenEscapableHandleScope、OH_JSVM_CloseEscapableHandleScope、OH_JSVM_EscapeHandle的样例方法 115static JSVM_Value EscapableHandleScopeTest(JSVM_Env env, JSVM_CallbackInfo info) 116{ 117 // 创建一个可逃逸的句柄作用域 118 JSVM_EscapableHandleScope scope = nullptr; 119 JSVM_Status status = OH_JSVM_OpenEscapableHandleScope(env, &scope); 120 if (status != JSVM_OK) { 121 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_OpenEscapableHandleScope: failed"); 122 return nullptr; 123 } 124 // 在可逃逸的句柄作用域内创建一个obj 125 JSVM_Value obj = nullptr; 126 OH_JSVM_CreateObject(env, &obj); 127 // 在对象中添加属性 128 JSVM_Value value = nullptr; 129 OH_JSVM_CreateStringUtf8(env, "Test jsvm_escapable_handle_scope", JSVM_AUTO_LENGTH, &value); 130 OH_JSVM_SetNamedProperty(env, obj, "name", value); 131 // 调用OH_JSVM_EscapeHandle将对象逃逸到作用域之外 132 JSVM_Value escapedObj = nullptr; 133 OH_JSVM_EscapeHandle(env, scope, obj, &escapedObj); 134 // 关闭可逃逸的句柄作用域,清理资源 135 status = OH_JSVM_CloseEscapableHandleScope(env, scope); 136 if (status != JSVM_OK) { 137 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_CloseEscapableHandleScope: failed"); 138 return nullptr; 139 } 140 // 此时的escapedObj已逃逸,可以在作用域外继续使用escapedObj 141 bool result = false; 142 OH_JSVM_CreateStringUtf8(env, "name", JSVM_AUTO_LENGTH, &value); 143 OH_JSVM_HasProperty(env, escapedObj, value, &result); 144 if (result) { 145 OH_LOG_INFO(LOG_APP, "JSVM EscapableHandleScopeTest: success"); 146 } 147 return escapedObj; 148} 149 150// EscapableHandleScopeTest注册回调 151static JSVM_CallbackStruct param[] = { 152 {.callback = EscapableHandleScopeTest, .data = nullptr}, 153}; 154static JSVM_CallbackStruct *method = param; 155// EscapableHandleScopeTest方法别名,供JS调用 156static JSVM_PropertyDescriptor descriptor[] = { 157 {"escapableHandleScopeTest", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 158}; 159 160const char *srcCallNative = "escapableHandleScopeTest()"; 161``` 162<!-- @[oh_jsvm_open_escapable_handle_scope_close_escapable_handle_scope_escape_handle](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmUsageGuide/JsvmLifeCycle/openescapablehandlescope/src/main/cpp/hello.cpp) --> 163 164预期输出 165 166``` 167JSVM EscapableHandleScopeTest: success 168``` 169 170### OH_JSVM_CreateReference、OH_JSVM_DeleteReference、OH_JSVM_GetReferenceValue 171 1721. 调用 OH_JSVM_CreateReference 为 JavaScript 变量创建一个引用,以延长其生命周期。 1732. 调用 OH_JSVM_GetReferenceValue 获取与引用关联的 JavaScript 变量。 1743. 调用 OH_JSVM_DeleteReference 删除传入的引用。 175 176调用者需要自己管理引用生命周期,引用有效期间 JavaScript 变量不会被垃圾回收处理。 177 178### OH_JSVM_ReferenceRef、OH_JSVM_ReferenceUnref 179 180增加/减少传入的引用的引用计数,并获取新的计数。当引用计数被置为 0 后,对于可以被设置为弱引用的 JavaScript 类型(对象、函数、外部变量),引用将被置为弱引用,在垃圾回收机制认为必要的时候该变量会被回收,当变量被回收后,调用 OH_JSVM_GetReferenceValue 会获得 C NULL;对于不可被置为弱引用的 JavaScript 类型,该引用会被清除,调用 OH_JSVM_GetReferenceValue 会获得 C NULL。 181 182cpp 部分代码: 183 184```cpp 185static JSVM_Value UseReference(JSVM_Env env, JSVM_CallbackInfo info) 186{ 187 // 创建 JavaScript 对象 188 JSVM_Value obj = nullptr; 189 OH_JSVM_CreateObject(env, &obj); 190 JSVM_Value value = nullptr; 191 OH_JSVM_CreateStringUtf8(env, "UseReference", JSVM_AUTO_LENGTH, &value); 192 OH_JSVM_SetNamedProperty(env, obj, "name", value); 193 194 JSVM_Ref g_ref = nullptr; 195 // 创建对JavaScript对象的引用 196 JSVM_Status status = OH_JSVM_CreateReference(env, obj, 1, &g_ref); 197 if (status != JSVM_OK) { 198 return nullptr; 199 } 200 201 // 增加传入引用的引用计数并返回生成的引用计数 202 uint32_t result = 0u; 203 OH_JSVM_ReferenceRef(env, g_ref, &result); 204 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_ReferenceRef, count = %{public}d.", result); 205 if (result != 2) { 206 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_ReferenceRef: failed"); 207 return nullptr; 208 } 209 210 // 减少传入引用的引用计数并返回生成的引用计数 211 uint32_t num = 0u; 212 OH_JSVM_ReferenceUnref(env, g_ref, &num); 213 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_ReferenceUnref, count = %{public}d.", num); 214 if (num != 1) { 215 return nullptr; 216 } 217 218 JSVM_Value object = nullptr; 219 // 通过调用OH_JSVM_GetReferenceValue获取引用的JavaScript对象 220 status = OH_JSVM_GetReferenceValue(env, g_ref, &object); 221 if (status != JSVM_OK) { 222 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_GetReferenceValue: failed"); 223 return nullptr; 224 } 225 226 // 不再使用引用,通过调用OH_JSVM_DeleteReference删除对JavaScript对象的引用 227 status = OH_JSVM_DeleteReference(env, g_ref); 228 if (status != JSVM_OK) { 229 OH_LOG_ERROR(LOG_APP, "JSVM OH_JSVM_DeleteReference: failed"); 230 return nullptr; 231 } 232 233 // 将获取到的对象返回 234 OH_LOG_INFO(LOG_APP, "JSVM UseReference success"); 235 return object; 236} 237 238// CreateReference、UseReference、DeleteReference注册回调 239static JSVM_CallbackStruct param[] = { 240 {.callback = UseReference, .data = nullptr}, 241}; 242static JSVM_CallbackStruct *method = param; 243// CreateReference、UseReference、DeleteReference方法别名,供JS调用 244static JSVM_PropertyDescriptor descriptor[] = { 245 {"useReference", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 246}; 247 248const char *srcCallNative = "useReference()"; 249``` 250<!-- @[oh_jsvm_reference_ref_and_oh_jsvm_reference_unref](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmUsageGuide/JsvmLifeCycle/referenceref/src/main/cpp/hello.cpp) --> 251 252预期结果: 253 254``` 255JSVM OH_JSVM_ReferenceRef, count = 2. 256JSVM OH_JSVM_ReferenceUnref, count = 1. 257JSVM UseReference success 258``` 259 260### OH_JSVM_AddFinalizer 261为 JavaScript 对象添加 JSVM_Finalize 回调,当 JavaScript 对象被垃圾回收时执行函数回调,该接口通常被用于释放与 JavaScript 对象相关的原生对象。如果传入的参数类型不是 JavaScript 对象,该接口调用失败并返回错误码。 262Finalizer 方法被注册后无法取消,如果在调用 OH_JSVM_DestroyEnv 前均未被执行,则在 OH_JSVM_DestroyEnv 时执行。 263 264cpp 部分代码: 265 266```cpp 267static int AddFinalizer(JSVM_VM vm, JSVM_Env env) { 268 // 打开 handlescope 269 JSVM_HandleScope handleScope; 270 CHECK_RET(OH_JSVM_OpenHandleScope(env, &handleScope)); 271 // 创建 object 并设置回调 272 JSVM_Value obj = nullptr; 273 CHECK_RET(OH_JSVM_CreateObject(env, &obj)); 274 CHECK_RET(OH_JSVM_AddFinalizer( 275 env, obj, nullptr, 276 [](JSVM_Env env, void *data, void *hint) -> void { 277 // Finalizer 方法,可在该方法中清理 Native 对象 278 OH_LOG_INFO(LOG_APP, "JSVM: finalizer called."); 279 }, 280 nullptr, nullptr)); 281 OH_LOG_INFO(LOG_APP, "JSVM: finalizer added."); 282 // 关闭 handlescope,触发 GC,GC 时 Finalizer 会被调用 283 CHECK_RET(OH_JSVM_CloseHandleScope(env, handleScope)); 284 OH_LOG_INFO(LOG_APP, "JSVM: before call gc."); 285 CHECK_RET(OH_JSVM_MemoryPressureNotification(env, JSVM_MemoryPressureLevel::JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL)); 286 OH_LOG_INFO(LOG_APP, "JSVM: after call gc."); 287 288 return 0; 289} 290 291static JSVM_Value RunDemo(JSVM_Env env, JSVM_CallbackInfo info) { 292 JSVM_VM vm; 293 OH_JSVM_GetVM(env, &vm); 294 if (AddFinalizer(vm, env) != 0) { 295 OH_LOG_INFO(LOG_APP, "Run PromiseRegisterHandler failed"); 296 } 297 298 return nullptr; 299} 300 301// RunDemo注册回调 302static JSVM_CallbackStruct param[] = { 303 {.data = nullptr, .callback = RunDemo}, 304}; 305static JSVM_CallbackStruct *method = param; 306// RunDemo方法别名,供JS调用 307static JSVM_PropertyDescriptor descriptor[] = { 308 {"RunDemo", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 309}; 310 311// 样例测试js 312const char *srcCallNative = R"JS(RunDemo();)JS"; 313``` 314<!-- @[oh_jsvm_add_finalizer](https://gitcode.com/openharmony/applications_app_samples/blob/master/code/DocsSample/ArkTS/JSVMAPI/JsvmUsageGuide/JsvmLifeCycle/addfinalizer/src/main/cpp/hello.cpp) --> 315 316预期结果: 317```ts 318JSVM: finalizer added. 319JSVM: before call gc. 320JSVM: finalizer called. 321JSVM: after call gc. 322```