1# 使用扩展的Node-API接口在当前线程中创建、切换和销毁上下文环境 2<!--Kit: NDK--> 3<!--Subsystem: arkcompiler--> 4<!--Owner: @xliu-huanwei; @shilei123; @huanghello--> 5<!--Designer: @shilei123--> 6<!--Tester: @kirl75; @zsw_zhushiwei--> 7<!--Adviser: @fang-jinxu--> 8在应用被拉起时,应用的主线程即为一个ArkTS线程,该线程中存在一个由系统管理的上下文环境,当ArkTS需要和C/C++交互时,在C/C++侧,napi_env即代表该上下文环境,每个上下文环境中存在着独立的globalThis对象。开发者可以通过使用Node-API中的扩展接口napi_create_ark_context和napi_destroy_ark_context在当前线程中创建和销毁新的上下文环境,这些新创建的上下文环境和线程中原始的上下文环境共用一个运行时虚拟机。需要注意的是napi_create_ark_context接口仅仅是创建新的上下文环境,而不是创建一个新的运行时,同时通过该接口创建上下文环境,需要通过napi_destroy_ark_context接口销毁,否则会造成内存泄漏。当然ArkTS线程的原始上下文环境不能通过napi_destroy_ark_context接口销毁。当需要切换到指定的上下文环境时,可以调用Node-API中的扩展接口napi_switch_ark_context来切换到指定的上下文环境。开发者可以在一个新的上下文环境中访问globalThis上的某些属性方法,也可以在访问完之后,切回到原先的上下文环境保证上下文环境的隔离。 9 10## 场景介绍 11开发者可以通过napi_create_ark_context接口在当前的线程中创建新的上下文环境,该上下文环境拥有独立的globalThis对象。这意味着当前线程上原始的上下文环境和新创建的上下文环境是相互隔离的,即上下文环境中的globalThis对象不同。开发者可以通过新创建的上下文环境进行模块化加载,加载生成的模块化对象将挂载在当前上下文环境中的globalThis对象上,不同的模块加载在不同的上下文环境中,可以避免一个模块对globalThis对象的上属性的修改影响到另一个模块对globalThis对象上属性的访问。当然部分的Node-API标准接口和扩展接口也进行上下文的适配,这意味着调用这些Node-API的接口,上下文环境会根据接口入参的napi_env来主动进行上下文的切换动作,开发者不需要主动调用napi_switch_ark_context来进行上下文环境的切换,需要注意的是从C++/C侧回到ArkTS侧时,要主动调用napi_switch_ark_context接口将上下文环境切回到对应的上下文环境,否则会造成ArkTS侧的代码执行在另外一个上下文环境导致不可预知的稳定性问题。 12 13## 支持多运行时上下文环境调用的NAPI接口 14下列表格中的NAPI接口都支持在多上下文的环境中执行,其中部分NAPI接口会主动进行上下文的切换,即使调用方没有主动调用napi_switch_ark_context来切换运行时上下文,这部分接口仍然可以通过比较当前运行的上下文环境和接口指定的运行时环境是否一致来决定是否切换上下文环境,如果上下文环境不一致,这些NAPI接口会将当前的运行时环境切换成接口参数指定的上下文环境。当然不涉及主动切换上下文环境的接口意味着这部分接口和运行时上下文无关,使用任意一个有效的上下文环境都行正常执行。 15 16| 接口 | 是否会主动进行上下文切换 | 17| -------- | -------- | 18|napi_module_register | 否 | 19|napi_fatal_error | 否 | 20|napi_async_init | 是 | 21|napi_async_destroy | 否 | 22|napi_make_callback | 是 | 23|napi_create_buffer | 是 | 24|napi_create_external_buffer | 是 | 25|napi_create_buffer_copy | 是 | 26|napi_is_buffer | 否 | 27|napi_get_buffer_info | 否 | 28|napi_create_async_work | 是 | 29|napi_delete_async_work | 否 | 30|napi_queue_async_work | 否 | 31|napi_cancel_async_work | 否 | 32|napi_get_node_version | 否 | 33|napi_fatal_exception | 否 | 34|napi_add_env_cleanup_hook | 否 | 35|napi_remove_env_cleanup_hook | 否 | 36|napi_open_callback_scope | 否 | 37|napi_close_callback_scope | 否 | 38|napi_create_threadsafe_function | 是 | 39|napi_get_threadsafe_function_context | 否 | 40|napi_call_threadsafe_function | 否 | 41|napi_acquire_threadsafe_function | 否 | 42|napi_release_threadsafe_function | 否 | 43|napi_unref_threadsafe_function | 否 | 44|napi_ref_threadsafe_function | 否 | 45|napi_add_async_cleanup_hook | 否 | 46|napi_remove_async_cleanup_hook | 否 | 47node_api_get_module_file_name | 否 | 48|napi_get_last_error_info | 否 | 49|napi_get_undefined | 否 | 50|napi_get_null | 否 | 51|napi_get_global | 否 | 52|napi_get_boolean | 否 | 53|napi_create_object | 否 | 54|napi_create_array | 否 | 55|napi_create_array_with_length | 否 | 56|napi_create_double | 否 | 57|napi_create_int32 | 否 | 58|napi_create_uint32 | 否 | 59|napi_create_int64 | 否 | 60|napi_create_string_latin1 | 否 | 61|napi_create_string_utf8 | 否 | 62|napi_create_string_utf16 | 否 | 63|napi_create_symbol | 否 | 64|napi_create_function | 是 | 65|napi_create_error | 是 | 66|napi_create_type_error | 是 | 67|napi_create_range_error | 是 | 68|napi_typeof | 否 | 69|napi_get_value_double | 否 | 70|napi_get_value_int32 | 否 | 71|napi_get_value_uint32 | 否 | 72|napi_get_value_int64 | 否 | 73|napi_get_value_bool | 否 | 74|napi_get_value_string_latin1 | 否 | 75|napi_get_value_string_utf8 | 否 | 76|napi_get_value_string_utf16 | 否 | 77|napi_coerce_to_bool | 否 | 78|napi_coerce_to_number | 是 | 79|napi_coerce_to_object | 是 | 80|napi_coerce_to_string | 是 | 81|napi_get_prototype | 是 | 82|napi_get_property_names | 是 | 83|napi_set_property | 是 | 84|napi_has_property | 是 | 85|napi_get_property | 是 | 86|napi_delete_property | 是 | 87|napi_has_own_property | 是 | 88|napi_set_named_property | 是 | 89|napi_has_named_property | 是 | 90|napi_get_named_property | 是 | 91|napi_set_element | 是 | 92|napi_has_element | 是 | 93|napi_get_element | 是 | 94|napi_delete_element | 是 | 95|napi_define_properties | 是 | 96|napi_is_array | 否 | 97|napi_get_array_length | 否 | 98|napi_strict_equals | 否 | 99|napi_call_function | 否 | 100|napi_new_instance | 是 | 101|napi_instanceof | 是 | 102|napi_get_cb_info | 否 | 103|napi_get_new_target | 否 | 104|napi_define_class | 是 | 105|napi_wrap | 是 | 106|napi_unwrap | 是 | 107|napi_remove_wrap | 是 | 108|napi_create_external | 否 | 109|napi_get_value_external | 否 | 110|napi_create_reference | 否 | 111|napi_delete_reference | 否 | 112|napi_reference_ref | 否 | 113|napi_reference_unref | 否 | 114|napi_get_reference_value | 否 | 115|napi_open_handle_scope | 否 | 116|napi_close_handle_scope | 否 | 117|napi_open_escapable_handle_scope | 否 | 118|napi_close_escapable_handle_scope | 否 | 119|napi_escape_handle | 否 | 120|napi_throw | 否 | 121|napi_throw_error | 是 | 122|napi_throw_type_error | 是 | 123|napi_throw_range_error | 是 | 124|napi_is_error | 否 | 125|napi_is_exception_pending | 否 | 126|napi_get_and_clear_last_exception | 否 | 127|napi_is_arraybuffer | 否 | 128|napi_create_arraybuffer | 否 | 129|napi_create_external_arraybuffer | 否 | 130|napi_get_arraybuffer_info | 否 | 131|napi_is_typedarray | 否 | 132|napi_create_typedarray | 否 | 133|napi_get_typedarray_info | 否 | 134|napi_create_dataview | 是 | 135|napi_is_dataview | 否 | 136|napi_get_dataview_info | 否 | 137|napi_get_version | 否 | 138|napi_create_promise | 是 | 139|napi_resolve_deferred | 否 | 140|napi_reject_deferred | 否 | 141|napi_is_promise | 否 | 142|napi_run_script | 否 | 143|napi_create_date | 是 | 144|napi_is_date | 否 | 145|napi_get_date_value | 否 | 146|napi_add_finalizer | 否 | 147|napi_create_bigint_int64 | 否 | 148|napi_create_bigint_uint64 | 否 | 149|napi_create_bigint_words | 是 | 150|napi_get_value_bigint_int64 | 否 | 151|napi_get_value_bigint_uint64 | 否 | 152|napi_get_value_bigint_words | 否 | 153|napi_get_all_property_names | 否 | 154|napi_set_instance_data | 否 | 155|napi_get_instance_data | 否 | 156|napi_detach_arraybuffer | 否 | 157|napi_is_detached_arraybuffer | 否 | 158|napi_type_tag_object | 否 | 159|napi_check_object_type_tag | 是 | 160|napi_object_freeze | 是 | 161|napi_object_seal | 是 | 162|napi_run_script_path | 是 | 163|napi_queue_async_work_with_qos | 否 | 164|napi_load_module | 是 | 165|napi_create_object_with_properties | 是 | 166|napi_create_object_with_named_properties | 是 | 167|napi_coerce_to_native_binding_object | 是 | 168|napi_load_module_with_info | 是 | 169|napi_create_ark_runtime | 否 | 170|napi_destroy_ark_runtime | 否 | 171|napi_serialize | 是 | 172|napi_deserialize | 是 | 173|napi_delete_serialization_data | 否 | 174|napi_call_threadsafe_function_with_priority | 否 | 175|napi_wrap_enhance | 是 | 176 177## 不支持多运行时上下文环境调用的NAPI接口 178| 接口 | 多运行时上下文环境调用返回值 | 179| -------- | -------- | 180|napi_define_sendable_class | napi_invalid_arg | 181|napi_is_sendable | napi_invalid_arg | 182|napi_create_sendable_object_with_properties | napi_invalid_arg | 183|napi_wrap_sendable | napi_invalid_arg | 184|napi_wrap_sendable_with_size | napi_invalid_arg | 185|napi_unwrap_sendable | napi_invalid_arg | 186|napi_remove_wrap_sendable | napi_invalid_arg | 187|napi_create_sendable_array | napi_invalid_arg | 188|napi_create_sendable_array_with_length | napi_invalid_arg | 189|napi_create_sendable_arraybuffer | napi_invalid_arg | 190|napi_create_sendable_typedarray | napi_invalid_arg | 191|napi_run_event_loop | napi_invalid_arg | 192|napi_stop_event_loop | napi_invalid_arg | 193|napi_get_uv_event_loop | napi_invalid_arg | 194 195### 示例代码 196- 模块注册 197 ```c++ 198 // napi_init.cpp 199 #include "napi/native_api.h" 200 #include "hilog/log.h" 201 202 static napi_value NAPI_Global_callFunctionInContext(napi_env env, napi_callback_info info) 203 { 204 napi_status status = napi_ok; 205 size_t argc = 1; 206 napi_value args[1] = {nullptr}; 207 if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) { 208 return nullptr; 209 } 210 // 在原始上下文中加载模块plugin1.ets 211 napi_value plugin1 = nullptr; 212 status = napi_load_module_with_info(env, "entry/src/main/ets/pages/plugin1", "com.example.myapplication/entry", &plugin1); 213 if (status != napi_ok) { 214 OH_LOG_INFO(LOG_APP, "load plugin1 failed"); 215 } 216 // 获取模块plugin1中的方法GetLocation 217 napi_value getLocation1 = nullptr; 218 status = napi_get_named_property(env, plugin1, "GetLocation", &getLocation1); 219 if (status != napi_ok) { 220 OH_LOG_INFO(LOG_APP, "obtain GetLocation from plugin1 failed"); 221 } 222 // 创建新的上下文环境newEnv2 223 napi_env newEnv2 = nullptr; 224 status = napi_create_ark_context(env, &newEnv2); 225 if (status != napi_ok) { 226 return nullptr; 227 } 228 // 主动切换到新的上下文环境newEnv2 229 status = napi_switch_ark_context(newEnv2); 230 if (status != napi_ok) { 231 OH_LOG_INFO(LOG_APP, "switch to newEnv2 failed"); 232 } 233 napi_value plugin2 = nullptr; 234 // 在新的上下文环境中加载模块plugin2.ets 235 status = napi_load_module_with_info(newEnv2, "entry/src/main/ets/pages/plugin2", "com.example.myapplication/entry", 236 &plugin2); 237 if (status != napi_ok) { 238 OH_LOG_INFO(LOG_APP, "load plugin2 failed"); 239 } 240 241 napi_value getLocation2 = nullptr; 242 status = napi_get_named_property(newEnv2, plugin2, "GetLocation", &getLocation2); 243 if (status != napi_ok) { 244 OH_LOG_INFO(LOG_APP, "obtain GetLocation from plugin2 failed"); 245 } 246 247 // 在新上下文环境中执行ArkTS侧的方法getLocation, 入参为模块plugin2中的方法GetLocation 248 napi_value result = nullptr; 249 napi_value args2[1] = {}; 250 args2[0] = getLocation2; 251 252 status = napi_call_function(newEnv2, nullptr, args[0], 1, args2, &result); 253 if (status != napi_ok) { 254 OH_LOG_INFO(LOG_APP, "call function of env failed"); 255 } 256 int32_t ret = 0; 257 status = napi_get_value_int32(newEnv2, result, &ret); 258 if (status != napi_ok) { 259 OH_LOG_INFO(LOG_APP, "napi_get_value_int32 of env failed"); 260 } else { 261 // plugin2的上下文中globalThis.a为3000 262 OH_LOG_INFO(LOG_APP, "ret is %{public}d", ret); // 3000 263 } 264 // 主动切回原始上下文环境env 265 status = napi_switch_ark_context(env); 266 if (status != napi_ok) { 267 OH_LOG_INFO(LOG_APP, "switch to env failed"); 268 } 269 args2[0] = getLocation1; 270 status = napi_call_function(env, nullptr, args[0], 1, args2, &result); 271 if (status != napi_ok) { 272 return nullptr; 273 } 274 // 获取GetLocation接口调用之后的返回值 275 ret = 0; 276 status = napi_get_value_int32(env, result, &ret); 277 if (status != napi_ok) { 278 return nullptr; 279 } else { 280 // plugin1的上下文中globalThis.a为2000 281 OH_LOG_INFO(LOG_APP, "ret is %{public}d", ret); // 2000 282 } 283 // 销毁创建的上下文环境 284 status = napi_destroy_ark_context(newEnv2); 285 if (status != napi_ok) { 286 return nullptr; 287 } 288 return result; 289 } 290 291 // 模块注册 292 EXTERN_C_START 293 static napi_value Init(napi_env env, napi_value exports) { 294 napi_property_descriptor desc[] = { 295 {"callFunctionInContext", nullptr, NAPI_Global_callFunctionInContext, 296 nullptr, nullptr, nullptr, napi_default, nullptr} 297 }; 298 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 299 return exports; 300 } 301 EXTERN_C_END 302 303 static napi_module demoModule = { 304 .nm_version = 1, 305 .nm_flags = 0, 306 .nm_filename = nullptr, 307 .nm_register_func = Init, 308 .nm_modname = "entry", 309 .nm_priv = ((void*)0), 310 .reserved = { 0 }, 311 }; 312 313 extern "C" __attribute__((constructor)) void RegisterEntryModule(void) 314 { 315 napi_module_register(&demoModule); 316 } 317 ``` 318- 接口声明 319 ```ts 320 // index.d.ts 321 export const callFunctionInContext: (func: (func:()=>number)=>{}) => number; 322 ``` 323 324- 编译配置 3251. CMakeLists.txt文件需要按照如下配置 326 ``` 327 // CMakeLists.txt 328 # the minimum version of CMake. 329 cmake_minimum_required(VERSION 3.5.0) 330 project(MyApplication8) 331 332 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 333 334 if(DEFINED PACKAGE_FIND_FILE) 335 include(${PACKAGE_FIND_FILE}) 336 endif() 337 338 add_definitions( "-DLOG_DOMAIN=0xd0d0" ) 339 add_definitions( "-DLOG_TAG=\"testTag\"") 340 341 include_directories(${NATIVERENDER_ROOT_PATH} 342 ${NATIVERENDER_ROOT_PATH}/include) 343 344 add_library(entry SHARED napi_init.cpp) 345 target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so) 346 ``` 347 3482. 需要在工程的build-profile.json5文件中进行以下配置 349 ```json 350 { 351 "buildOption" : { 352 "arkOptions" : { 353 "runtimeOnly" : { 354 "sources": [ 355 "./src/main/ets/pages/plugin1.ets", 356 "./src/main/ets/pages/plugin2.ets" 357 ] 358 } 359 } 360 } 361 } 362 ``` 363 364- ArkTS代码示例 365 ```ts 366 // index.ets 367 import testNapi from "libentry.so" 368 // 该接口用于执行模块plugin1或plugin2中的GetLocation方法 369 function getLocation(func: () => number) { 370 return func(); 371 } 372 testNapi.callFunctionInContext(getLocation) 373 ``` 374 ```ts 375 // ets/pages/plugin1.ets 376 globalThis.a = 2000; 377 378 export function GetLocation() : number { 379 return globalThis.a; 380 } 381 ``` 382 ```ts 383 // ets/pages/plugin2.ets 384 globalThis.a = 3000; 385 386 export function GetLocation() : number { 387 return globalThis.a; 388 } 389 ```