diff --git a/zh-cn/application-dev/napi/napi-faq-about-common-basic.md b/zh-cn/application-dev/napi/napi-faq-about-common-basic.md index 84317bc21b219be4db6eee5409e01bcde044b067..7ee6f6af049b08f20b91fda72917bf7091564ad8 100644 --- a/zh-cn/application-dev/napi/napi-faq-about-common-basic.md +++ b/zh-cn/application-dev/napi/napi-faq-about-common-basic.md @@ -23,54 +23,49 @@ extern "C" __attribute__((constructor)) void RegisterEntryModule() - 排查建议: 1. 可根据以下文档进行排查: -[ArkTS/JS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not callable或明确的Error message](napi-faq-about-common-basic.md#arkts侧报错显示undefined) -[模块注册与模块命名](napi-guidelines.md#模块注册与模块命名) +[ArkTS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not callable或明确的Error message](napi-faq-about-common-basic.md#ArkTS侧报错显示undefined) +[模块注册与模块命名](napi-guidelines.md#模块注册与模块命名) 2. 同时也可参考动态加载能力是否可以满足该场景 -[napi_load_module_with_info支持的场景](use-napi-load-module-with-info.md#napi_load_module_with_info支持的场景) -[napi_load_module支持的场景](use-napi-load-module.md#napi_load_module支持的场景) +[napi_load_module_with_info支持的场景](use-napi-load-module-with-info.md#napi_load_module_with_info支持的场景) +[napi_load_module支持的场景](use-napi-load-module.md#napi_load_module支持的场景) -## 在大量需要调用ArkTS方法进行通信的场景如何保证异步任务的有序性 +## 在大量需要调用ArkTS方法进行通信的场景中如何保证异步任务的有序性 -- 具体问题:在大量需要通过c++调用ArkTS方法进行通信的场景,如何保证异步任务的有序性? +- 具体问题:在大量需要通过C++调用ArkTS方法进行通信的场景,如何保证异步任务的有序性? - 参考方案: -可参考线程安全函数来实现,napi_call_threadsafe_function可保证异步任务执行顺序, 需要注意的是这些异步任务会抛回到ArkTS线程顺序执行,如果是抛回到主线程,异步任务的执行时间过长可能导致应用冻结退出,所以不建议将长耗时的任务通过线程安全函数抛回到主线程执行。 +可参考线程安全函数来实现,napi_call_threadsafe_function可保证异步任务执行顺序, 需要注意的是这些异步任务会投递到ArkTS线程顺序执行,如果是投递到主线程,异步任务的执行时间过长可能导致应用冻结退出,所以不建议将长耗时的任务通过线程安全函数投递到主线程执行。 [使用Node-API接口进行线程安全开发](use-napi-thread-safety.md) -此外,napi中常见的抛任务方法的差异如下: +此外,Node-API中常见的抛任务方法的差异如下: -1. napi_async_work系列接口:只能保证execute_cb执行在complete_cb之前,对于不同的async_work,时序无法保证 +1. napi_async_work系列接口只能保证execute_cb在complete_cb之前执行,但无法保证不同async_work的时序。 [napi_queue_async_work_with_qos](../../application-dev/reference/native-lib/napi.md#napi_queue_async_work_with_qos)是在普通napi_queue_async_work的基础上,支持自定义qos优先级,而这里只是指定libuv调度任务时使用线程的优先级,不是指任务的优先级,所以也无法保证任务的时序。 -2. napi_threadsafe_function系列接口:接口内部维护了一个队列,是保序的。 -napi_call_threadsafe_function:先入先出 -napi_call_threadsafe_function_with_priority:根据自己指定的任务优先级执行 +2. napi_threadsafe_function系列接口内部维护了一个队列,可以保证任务执行的顺序。 +napi_call_threadsafe_function按先入先出的顺序执行。 +napi_call_threadsafe_function_with_priority根据指定的入队方式执行。 [使用Node-API接口从异步线程向ArkTS线程投递指定优先级和入队方式的的任务](use-call-threadsafe-function-with-priority.md) ## 是否存在便捷的回调ArkTS的方式 - 具体描述: -在进行多线程开发时,ArkTS函数只能在其创建线程上执行,c++线程不能直接通过napi_call_function的形式直接调用ArkTS回调,是否存在便捷的方法? -可参考文档: +在进行多线程开发时,ArkTS函数只能在其创建线程上执行,C++线程不能直接通过napi_call_function的形式直接调用ArkTS回调,是否存在便捷的方法? +可参考文档: [Native侧子线程与UI主线程通信](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-native-sub-main-comm) [使用Node-API接口进行异步任务开发](use-napi-asynchronous-task.md) ## 如何在C++代码中回调ArkTS方法 -- 参考文档: +- 参考文档: [如何在C++调用从ArkTS传递过来的function](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-ndk-26) -## 由于工作线程 A 长时间持有锁M,而主线程又在等待获取锁M,形成循环等待条件,导致系统进入死锁状态 - -- 参考方案: -可使用napi_threadsafe_function系列接口,具体可参考前文问题二的解决方案 - ## 如何确保数据类型的正确映射与内存管理的安全性 - 具体问题:如何在遵循N-API单一返回值约束的前提下,安全、高效地将多个返回值(包括结构化数据和指针信息)传递给ArkTS运行时环境,并确保数据类型的正确映射与内存管理的安全性? - 参考实现: 尽管napi_value接口仅支持单一返回值,但开发者可通过该返回值封装所需的全部信息。 -比如通过napi_create_object,创建出一个ArkTS对象,用这个对象来承载返回的所有信息,number和string都可以通过napi_set_property/napi_set_named_property等属性设置的接口设置到这个对象上。native对象也可以通过napi_wrap接口和ArkTS对象进行绑定,后续在通过napi_unwrap取出来。 -此外,亦可使用ArkTS数组作为数据载体,其具有良好的灵活性。 +比如通过napi_create_object,创建出一个ArkTS对象,用这个对象来承载返回的所有信息,number和string都可以通过napi_set_property/napi_set_named_property等属性设置的接口设置到这个对象上。native对象也可以通过napi_wrap接口和ArkTS对象进行绑定,后续再通过napi_unwrap取出来。 +此外,还可以使用ArkTS数组作为数据载体,其具有良好的灵活性。 - 参考文档: [使用Node-API接口进行object相关开发](use-napi-about-object.md) [使用Node-API接口进行array相关开发](use-napi-about-array.md) @@ -125,35 +120,35 @@ napi_value NapiGenericFailure(napi_env env, napi_callback_info) } ``` -## Native层调用ts层对象方法必须传入一个function给Native层吗 +## Native层调用ArkTS层对象方法必须传入一个function给Native层吗 -- 具体问题:【NAPI】Native层调用ts层对象方法,必须传入一个function给Native层吗? +- 具体问题:Node-API的Native层调用ArkTS层对象方法,必须传入一个function给Native层吗? - 参考方案: -如果想要在Native层调用ts层对象方法,则Native层需获取该TS Function对象。 -获取的途径也有很多,比如: -1. 通过传递的方式,ts层传给Native层,也就是问题描述的方案 -2. 可以把这个ts function通过属性设置方式绑定到Native层可访问的对象上,这样Native层通过这个对象也能拿到function进行调用 -3. napi层也提供了一个创建ts function的能力,即napi_create_function,可以直接在Native层中创建出来,这样,Native层自然就能拿到这个ts function +如果想要在Native层调用ArkTS层对象方法,则Native层需获取该ArkTS Function对象。 +获取的途径有多种,比如: +1. 通过传递的方式,ArkTS层传给Native层,也就是问题描述的方案 +2. 可以把这个ArkTS function通过属性设置方式绑定到Native层可访问的对象上,这样Native层通过这个对象也能拿到function进行调用 +3. Node-API层也提供了一个创建ArkTS Function对象的能力,即napi_create_function,可以直接在Native层中创建出来,这样,Native层自然就能拿到这个ArkTS Function对象 ## 是否能调用ets的方法并获取到结果 -- 具体问题:在c++通过pthread或std::thread创建的线程,是否能调用ets的方法并获取到结果? -问题分析: -如果是直接创建出来的c++线程,这个线程是没有ArkTS运行环境的,也就是该线程上没有对应的napi env,所以无法直接在刚创建出来的c++线程上直接运行ets方法并获取到结果。 +- 具体问题:在C++通过pthread或std::thread创建的线程,是否能调用ets的方法并获取到结果? +问题分析: +如果是直接创建出来的C++线程,该线程没有ArkTS运行环境,即该线程上没有对应的napi_env。直接在该线程上调用ArkTS方法,存在多线程安全问题,属于未定义行为。 - 解决方案参考: -1. 使用napi_threadsafe_function系列的napi接口,这系列接口,相当于在c++线程抛任务回到ArkTS线程执行ets方法 +1. 使用napi_threadsafe_function系列的Node-API接口,这系列接口,相当于在C++线程抛任务回到ArkTS线程执行ets方法 [使用Node-API接口进行线程安全开发](use-napi-thread-safety.md) -2. 在c++线程创建出ArkTS运行环境 +2. 在C++线程创建出ArkTS运行环境 [使用Node-API接口创建ArkTS运行时环境](use-napi-ark-runtime.md) ## 是否有不拷贝的napi_get_value_string_utf8接口或者能力 -- 具体问题:当前napi的napi_get_value_string_utf8每次调用的时候都要进行拷贝,是否有不拷贝的napi_get_value_string_utf8接口或者能力? +- 具体问题:当前Node-API的napi_get_value_string_utf8每次调用的时候都要进行拷贝,是否有不拷贝的napi_get_value_string_utf8接口或者能力? - 问题解答: -当前版本暂不支持该功能,每次napi_get_value_string_utf8都是需要有一个拷贝过程的。 +不支持该功能,每次napi_get_value_string_utf8都需要有一个拷贝过程。 -拷贝是必要的,因为会涉及到string生命周期。当触发GC的时候,ArkTS对象可能会在虚拟机里面被搬移,可能搬移到其他地方,也可能直接对象被回收。如果直接返回一个类似char*的地址,对象被移动或回收后,那这个地址的指向就不再是之前的字符串了,此时再用这个地址去解引用很容易崩溃。 +拷贝是必要的,因为会涉及到string生命周期。当触发GC的时候,ArkTS对象可能会在虚拟机里面被搬移,可能搬移到其他地方,也可能直接对象被回收。如果直接返回类似char*的地址,对象被移动或回收后,原地址的指向的内存可能发生变化。 ## 多线程下napi_env的使用注意事项 @@ -172,37 +167,33 @@ napi_value NapiGenericFailure(napi_env env, napi_callback_info) ## napi_call_threadsafe_function执行顺序不符合预期 - 问题描述: -napi_call_threadsafe_function执行结果不符合预期 +napi_call_threadsafe_function执行顺序不符合预期 原本期望的执行顺序是 a -> b -> c -posttask(a); -posttask(b); -posttask(c); -但是实际的执行顺序是 b -> a -> c +posttask(a); +posttask(b); +posttask(c); +但是实际的执行顺序是 b -> a -> c -- 排查方向: -1. 是不是用的同一个napi_threadsafe_function,若使用不同实例,则无法保障与调用顺序一致; -注:对于同一个napi_threadsafe_function来说,napi_call_threadsafe_function是保序的,接口内维护了一个队列,先调用就会先执行。 -注:不同实例是先创建,先执行,但是若使用不同实例,得保证对应关系。 -2. 是否能保证实际call的顺序是a -> b -> c; -3. 使用了napi_call_threadsafe_function_with_priority,该接口是向主线程的事件循环中投递任务, 由于主线程存在不同优先级的队列, 不同的优先级的任务是无法保证时序的,同意优先级的任务,由于是在同一事件队列,可以保证时序。 +- 排查方向: +1. 是否使用的是同一个napi_threadsafe_function,若使用不同实例,则无法保障执行顺序一致; +注:对于同一个napi_threadsafe_function来说,napi_call_threadsafe_function是保序的,接口内维护了一个队列,先调用就会先执行。 +2. 是否能保证实际napi_threadsafe_function的调用顺序是a -> b -> c; -参考文档: -[使用Node-API接口从异步线程向ArkTS线程投递指定优先级和入队方式的的任务](use-call-threadsafe-function-with-priority.md) ## ArkTS侧报错显示undefined 具体问题: -ArkTS/JS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not callable或明确的Error message +ArkTS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not callable或明确的Error message 1. 排查.cpp文件在注册模块时的模块名称与so的名称匹配一致。 如模块名为entry,则so的名字为libentry.so,napi_module中nm_modname字段应为entry,大小写与模块名保持一致。 2. 排查so是否加载成功。 - 应用启动时过滤模块加载相关日志,重点搜索"dlopen"关键字,确认是否有相关报错信息;常见加载失败原因有权限不足、so文件不存在以及so已拉入黑名单等,可根据以下关键错误日志确认问题。其中,多线程场景(worker、taskpool等)下优先检查模块实现中nm_modname是否与模块名一致,区分大小写。 + 应用启动时过滤模块加载相关日志,重点搜索"dlopen"关键字,确认是否有相关报错信息;常见加载失败原因有权限不足、so文件不存在以及so已拉入黑名单等,可根据以下关键错误日志确认问题。其中,多线程场景(如worker、taskpool等)下优先检查模块实现中nm_modname是否与模块名一致,区分大小写。 3. 排查依赖的so是否加载成功。 确定所依赖的其它so是否打包到应用中以及是否有权限打开。常见加载失败原因有权限不足、so文件不存在等。 4. 排查模块导入方式与so路径是否对应。 - 若JS侧导入模块的形式为: import xxx from '\@ohos.yyy.zzz',则该so将在/system/lib/module/yyy中找libzzz.z.so或libzzz_napi.z.so,若so不存在或名称无法对应,则报错日志中会出现dlopen相关日志。 + 若ArkTS侧导入模块的形式为: import xxx from '\@ohos.yyy.zzz',则该so将在/system/lib/module/yyy中找libzzz.z.so或libzzz_napi.z.so,若so不存在或名称无法对应,则报错日志中会出现dlopen相关日志。 注意,32位系统路径为/system/lib,64位系统路径为/system/lib64。 @@ -232,7 +223,7 @@ ArkTS/JS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not cal 解决此类问题有以下两种思路: - 若该异常开发者不关心,可以选择直接清除。 - 可直接使用napi接口napi_get_and_clear_last_exception,清理异常。调用时机:在打印occur exception need return日志的接口之前调用。 + 可直接使用Node-API接口napi_get_and_clear_last_exception,清理异常。调用时机:在打印occur exception need return日志的接口之前调用。 - 将该异常继续向上抛到ArkTS层,在ArkTS层进行捕获。 发生异常时,可以选择走异常分支, 确保不再走多余的Native逻辑 ,直接返回到ArkTS层。 @@ -245,7 +236,7 @@ ArkTS/JS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not cal ## Node-API接口返回值不是napi_ok时如何排查定位 -Node-API接口正常执行后,会返回一个napi_ok的状态枚举值,若napi接口返回值不为napi_ok,可从以下几个方面进行排查。 +Node-API接口正常执行后,会返回一个napi_ok的状态枚举值,若Node-API接口返回值不为napi_ok,可从以下几个方面进行排查。 - Node-API接口执行前一般会进行入参校验,首先进行的是判空校验。在代码中体现为: @@ -260,7 +251,7 @@ Node-API接口正常执行后,会返回一个napi_ok的状态枚举值,若na RETURN_STATUS_IF_FALSE(env, NativeValue->TypeOf() == Native_NUMBER, napi_number_expected); ``` -- 还有一些接口会对其执行结果进行校验。比如napi_call_function这个接口,其功能是执行一个ArkTS function,当ArkTS function中出现异常时,Node-API将会返回napi_pending_exception的状态值。 +- 还有一些接口会对其执行结果进行校验。比如napi_call_function这个接口,其功能是执行一个ArkTS Function,当ArkTS Function中出现异常时,Node-API将会返回napi_pending_exception的状态值。 ```cpp // 接口内部实现,经校验可返回状态值 @@ -271,16 +262,16 @@ Node-API接口正常执行后,会返回一个napi_ok的状态枚举值,若na - 还有一些状态值需要根据相应Node-API接口具体分析:确认具体的状态值,分析这个状态值在什么情况下会返回,再排查具体出错原因。 ## napi_wrap如何保证被wrap的对象按期望顺序析构 -问题:在使用`napi_wrap`把两个 C++ 对象包装成两个 JavaScript 对象的场景中,由于这两个 C++ 对象存在依赖关系,要求其中一个c++对象必须在另一个c++对象之前析构。然而,JavaScript 垃圾回收(GC)的时机不确定,直接在`napi_wrap`的`finalize_cb`回调里销毁 C++ 对象,没办法保证析构顺序符合要求。该如何保证两个c++对象析构的前后顺序? +问题:在使用`napi_wrap`把两个 C++ 对象包装成两个 JavaScript 对象的场景中,由于这两个 C++ 对象存在依赖关系,要求其中一个C++对象必须在另一个C++对象之前析构。然而,JavaScript 垃圾回收(GC)的时机不确定,直接在`napi_wrap`的`finalize_cb`回调里销毁 C++ 对象,没办法保证析构顺序符合要求。该如何保证两个C++对象析构的前后顺序? 参考方案: 先标记可释放状态,当A和B都为可释放状态时同时释放C++对象 -原理:将所有依赖对象的释放逻辑集中在最后一个被销毁的 JS 对象的 finalize_cb 中处理。 +原理:将所有依赖对象的释放逻辑集中在最后一个被销毁的 ArkTS 对象的 finalize_cb 中处理。 实现步骤: 在 jsObjA 的 finalize_cb 中标记 cppObjA 为待销毁(不立即释放)。 在 jsObjB 的 finalize_cb 中标记 cppObjB 为待销毁(不立即释放)。 -jsObjA 和 jsObjB 都为待销毁状态时,按顺序销毁A和B。 -示例代码: +当 jsObjA 和 jsObjB 均处于待销毁状态时,按顺序销毁 cppObjA 和 cppObjB。 +示例代码: ```cpp struct ObjectPair { CppObjA* objA; diff --git a/zh-cn/application-dev/napi/napi-faq-about-memory-leak.md b/zh-cn/application-dev/napi/napi-faq-about-memory-leak.md index 0795403552fdee33c5c1803e540cedc33e3eb563..cb75dd218c3f553714ea6d28839931b277513ee1 100644 --- a/zh-cn/application-dev/napi/napi-faq-about-memory-leak.md +++ b/zh-cn/application-dev/napi/napi-faq-about-memory-leak.md @@ -6,32 +6,32 @@ - 检测方式: 可使用 DevEco Studio(IDE)提供的 Allocation 工具进行检测。 [基础内存分析:Allocation分析](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-insight-session-allocations) -napi_create_reference这个接口内部实现会new一个c++对象,因此,如果忘记使用napi_delete_reference接口,那这个new出来的c++对象也会泄漏,这时候就可以用Allocation工具来进行检测,这个工具会把未释放的对象的分配栈都打印出来,如果napi_ref泄漏了,可以在分配栈上看出来 +napi_create_reference这个接口内部实现会new一个C++对象,因此,如果忘记使用napi_delete_reference接口,那这个new出来的C++对象也会泄漏,这时候就可以用Allocation工具来进行检测,这个工具会把未释放的对象的分配栈都打印出来,如果napi_ref泄漏了,可以在分配栈上看出来 ## napi开发过程中遇见内存泄漏问题要怎么定位解决 -每次点击按钮内存都会增加一些,并且主动触发gc也不见回收,napi开发过程中的内存泄漏问题要怎么定位解决? +点击按钮时内存增加,即使主动触发GC也无法回收。如何在Node-API开发过程中定位和解决内存泄漏问题? - 解决建议: -先了解一下napi生命周期相关材料: -[使用Node-API接口进行生命周期相关开发](use-napi-life-cycle.md) -使用napi导致内存泄漏的常见原因: -1. napi_value不在handle_scope管控中,导致native持有的js对象不释放,尤其常见在直接使用uv_queue_work中。解决方案为添加 napi_open_handle_scope 和 napi_close_handle_scope 接口。 +需先了解Node-API生命周期机制,相关材料如下: +[使用Node-API接口进行生命周期相关开发](use-napi-life-cycle.md) +使用Node-API时导致内存泄漏的常见原因: +1. napi_value不在napi_handle_scope管理中,导致napi_value持有的ArkTS对象无法释放,该问题常见于直接使用uv_queue_work的场景中。解决方法是添加napi_open_handle_scope和napi_close_handle_scope接口。 [易错API的使用规范](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-coding-standard-api#section1219614634615) -此类泄漏可通过snapshot分析定位原因,泄漏的js对象distance为1,即不知道被谁持有,这种情况下一般就是被native(napi_value是个指针,指向native持有者)持有了,且napi_value不在handle_scope范围内。 -2. 使用了napi_create_reference为js对象创建了强引用(initial_refcount参数大于0),并且一直未delete,导致js对象一直被持有,无法被回收。napi_create_reference接口会new一个c++对象,因此这种泄漏通常表现为ArkTS与C++层的双重泄漏。可以使用Allocation工具抓c++泄漏栈,参考是否有napi_create_reference相关栈帧。 +此类泄漏可通过snapshot分析定位原因,泄漏的ArkTS对象distance为1,即不知道被谁持有,这种情况下一般就是被native(napi_value是个指针,指向native持有者)持有了,且napi_value不在handle_scope范围内。 +使用 `napi_create_reference` 为 ArkTS 对象创建了强引用(`initial_refcount` 参数大于 0),且一直未删除,导致 ArkTS 对象无法被回收。`napi_create_reference` 接口内部会创建一个 C++ 对象,因此这种泄漏通常表现为ArkTS对象与Native对象的双重泄漏。可以使用 Allocation 工具捕获Native对象泄漏栈,检查是否存在 `napi_create_reference` 相关的栈帧。 -[基础内存分析:Allocation分析](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-insight-session-allocations) +[基础内存分析:Allocation分析](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-insight-session-allocations) -3. 被其他存活js对象持有,这时候snapshot查看泄漏对象被谁持有。 +3. 被其他存活的ArkTS对象持有时,使用snapshot查看泄漏对象的持有者。 ## napi_threadsafe_function内存泄漏应该如何处理 `napi_threadsafe_function`(下文简称tsfn)在使用时,常常会调用 `napi_acquire_threadsafe_function` 来更改tsfn的引用计数,确保tsfn不会意外被释放。但在使用完成后,应该及时使用 `napi_tsfn_release` 模式调用 `napi_release_threadsafe_function` 方法,以确保在所有调用回调都执行完成后,其引用计数能回归到调用 `napi_acquire_threadsafe_function` 方法之前的水平。当其引用计数归为0时,tsfn才能正确的被释放。 -当在env即将退出,但tsfn的引用计数未被归零时,应该使用 `napi_tsfn_abort` 模式调用 `napi_release_threadsafe_function` 方法,确保在env释放后不再对tsfn进行持有及使用。在env退出后,继续持有tsfn进行使用,是一种未定义的行为,可能会触发崩溃。 +当env即将退出,但tsfn的引用计数未归零时,应使用 `napi_tsfn_abort` 模式调用 `napi_release_threadsafe_function` 方法,确保env释放后不再持有或使用tsfn。env退出后继续持有tsfn将导致未定义行为,可能引发崩溃。 如下代码将展示通过注册 `env_cleanup` 钩子函数的方式,以确保在env退出后不再继续持有tsfn。 @@ -57,7 +57,7 @@ napi_create_reference这个接口内部实现会new一个c++对象,因此, // 定义一个数据结构,模拟存储tsfn的场景 class MyTsfnContext { public: -// 因使用了napi方法, MyTsfnContext 应当只在js线程被构造 +// 因使用了Node-API方法, MyTsfnContext 应当只在ArkTS线程被构造 MyTsfnContext(napi_env env, napi_value workName) { // 注册env销毁钩子函数 napi_add_env_cleanup_hook(env, Cleanup, this); @@ -178,7 +178,7 @@ napi_value MyTsfnDemo(napi_env env, napi_callback_info info) { }; ``` -以下内容为主线程逻辑,主要用作创建worker线程和通知worker执行任务 +以下内容为主线程逻辑,主要用于创建 worker 线程并通知其执行任务。 ```ts // 主线程 Index.ets @@ -197,19 +197,19 @@ mWorker.postMessage({action: 'tsfn-demo'}); ``` -以下内容为Worker线程逻辑,主要用以触发Native任务 +以下内容为Worker线程逻辑,主要用于触发Native任务。 ```ts // worker.ets import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker'; -import napiModule from 'libentry.so'; // libentry.so: napi 库的模块名称 +import napiModule from 'libentry.so'; // libentry.so: Node-API 库的模块名称 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; workerPort.onmessage = (e: MessageEvents) => { const action: string | undefined = e.data?.action; if (action === 'tsfn-demo') { - // 触发 c++ 层的 tsfn demo + // 触发 C++ 层的 tsfn demo napiModule.myTsfnDemo(); // 通知主线程结束 worker workerPort.postMessage({action: 'kill'}); diff --git a/zh-cn/application-dev/napi/napi-faq-about-stability.md b/zh-cn/application-dev/napi/napi-faq-about-stability.md index d6eb797e2498603c0a89b47ca5b34e06ad2c2d98..3208dcce004101b1b152a15a604af447f73d987d 100644 --- a/zh-cn/application-dev/napi/napi-faq-about-stability.md +++ b/zh-cn/application-dev/napi/napi-faq-about-stability.md @@ -17,35 +17,35 @@ Tid:15894, Name:e.myapplication ``` - 定位问题: -使用napi时如果出现高概率闪退,崩溃栈顶在系统库libark_jsruntime.so,一般是开发者napi接口使用不当导致。 +使用Node-API时如果出现高概率闪退,崩溃栈顶在系统库libark_jsruntime.so,一般是开发者Node-API接口使用不当导致。 - 以下定位问题的思路,可作为参考: -1. 排查是否存在多线程安全问题(概率较大) -IDE中提供了相关开关,开启开关后,重新编译打包并运行,看看崩溃栈是不是符合下面这个文档的描述,如果是,那就是在使用napi时,存在多线程安全问题。 +1. 排查是否存在多线程安全问题(概率较大)。 +IDE中提供了相关开关,开启开关后,重新编译打包并运行,看看崩溃栈是不是符合下面这个文档的描述,如果是,那就是在使用Node-API时,存在多线程安全问题。 [常见多线程安全问题](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-ark-runtime-detection#section19357830121120) IDE开关: ![IDE多线程开关](figures/zh_cn_image_20-25-06-40-15-09.png) -2. 使用napi接口时入参非法导致。 -- 这种情况一般是崩溃栈上的so会很浅,so调用了某个具体的napi接口,比如调用了napi_call_function之类的接口,然后napi又调到了libark_jsruntime的so,然后直接崩溃在libark_jsruntime里面。 +2. 使用Node-API接口时入参非法导致。 +- 这种情况一般是崩溃栈上的so会很浅,so调用了某个具体的Node-API接口,比如调用了napi_call_function之类的接口,然后Node-API又调到了libark_jsruntime的so,然后直接崩溃在libark_jsruntime里面。 示例栈结构如下。 ```sh #01 /system/lib/platformsdk/libark_jsruntime.so #02 /system/lib/platformsdk/libark_jsruntime.so -#03 /system/lib/platformsdk/libace_napi.z.so(napi_set_named_property+170) -- napi的so,该位置显示具体调用报错的接口 +#03 /system/lib/platformsdk/libace_napi.z.so(napi_set_named_property+170) -- Node-API的so,该位置显示具体调用报错的接口 #04 /data/storage/el1/bundle/libs/arm/libentry.so -- 你的so ``` - 如果是入参问题,一般so在崩溃栈上的位置比较浅(不会跑到#10这种离栈顶很远的位置),不过也可以按照这个思路进行排查一下。 - 排查思路参考: a. 排查有没有napi_value未初始化,还没赋值成功,直接作为非法入参传递给接口了 -b. 排查有没有在这个易错API列表里面找到相应的篇章--[方舟运行时API](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-coding-standard-api#section1219614634615) +b. 排查有没有在这个易错API列表里面找到相应的篇章。[方舟运行时API](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-coding-standard-api#section1219614634615) ## 线程池中并发调用ArkTS方法如何处理线程安全问题 -- 现有个场景,ArkTS中有个类方法,对这个方法创建了napi_ref引用,现想在c++线程池中并发的调用ArkTS方法,请问 -1. 可以在c++创建的线程池中调用napi_ref缓存的ArkTS类方法吗? -只能在c++线程中将ArkTS任务抛回ts线程,这时候并不是同步调用,而是一个抛任务的动作。 -需要注意的是,这个ArkTS方法真正的执行动作只能在ts线程中完成,即,只能方法运行在创建的ts线程上。 +- 现有个场景,ArkTS中有个类方法,对这个方法创建了napi_ref引用,现想在C++线程池中并发的调用ArkTS方法,请问 +1. 可以在C++创建的线程池中调用napi_ref缓存的ArkTS类方法吗? +只能在C++线程中将ArkTS任务抛回ts线程,这时候并不是同步调用,而是一个抛任务的动作。 +需要注意的是,这个ArkTS方法真正的执行动作只能在ArkTS线程中完成,即,只能方法运行在对应的ArkTS线程上。 2. 回调到ArkTS要怎么确保线程安全? -上面提到,c++线程都是抛任务到ts线程,进而执行ts方法。关于线程安全,可参考napi线程安全文档,相关能力是有的。 +上面提到,C++线程都是抛任务到ArkTS线程,进而执行ArkTS方法。关于线程安全,可参考Node-API线程安全文档。 [使用Node-API接口进行线程安全开发](use-napi-thread-safety.md) 另外,开发过程中也可以打开多线程安全检查开关,这个开关可以拦截多线程安全问题。 [方舟多线程检测](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-ark-runtime-detection#section75786272088) @@ -58,11 +58,11 @@ b. 排查有没有在这个易错API列表里面找到相应的篇章--[方舟 1. 确认一下,是否napi_value出了scope还在使用,导致use-after-scope问题。 可参考文档: [方舟运行时API](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-coding-standard-api#section1219614634615) -2. 保存时建议使用napi_ref进行保存,而不是直接保存napi_value。 +2. 保存时建议使用napi_ref,而不是直接保存napi_value ## 是否存在获取最新napi_env的方法 -- 具体描述:有个场景,Native 层在较深的调用层级中需调用 ArkTS 方法,无法逐层传递 napi_env,直接缓存的话会有crash,崩溃栈如下: +- 具体描述:Native 层在较深的调用层级中需调用 ArkTS 方法,无法逐层传递 napi_env,直接缓存会导致崩溃。 ```sh #00 /system/lib/platformsdk/libark_jsruntime.so(panda::JSValueRef::IsFunction) #01 /system/lib/platformsdk/libace_napi.z.so(napi_call_function) @@ -71,19 +71,18 @@ b. 排查有没有在这个易错API列表里面找到相应的篇章--[方舟 ``` - 参考方案: 1. 关于保存napi_env: -napi没有提供直接获取napi_env的能力,只能通过传递一层一层传下来。一般不推荐保存napi_env,有两个原因: +Node-API没有提供直接获取napi_env的能力,只能通过逐层函数调用传递。一般不推荐保存napi_env,有两个原因: 其一,napi_env退出时候如果没有被使用方感知到,很容易出现use-after-free问题; -其二,napi_env和js线程时强绑定的,如果napi_env放在其他js线程使用,就会有多线程安全问题。 +其二,napi_env和ArkTS线程是强绑定的,如果napi_env放在其他ArkTS线程使用,就会有多线程安全问题。 可参考文档: [napi_env禁止缓存的原因是什么](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-ndk-73) -2. 该问题重点在于: -如果要强行保存env,就一定要感知env是否退出,用napi_add_env_cleanup_hook的回调去感知。同时在开发过程中把多线程检测开关打开,避免出现多线程安全问题。 -可参考多线程检测文档: -[常见多线程安全问题](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-ark-runtime-detection#section19357830121120) +2. 该问题的关键在于: +如果要强行保存env,必须感知env是否退出,可以使用napi_add_env_cleanup_hook的回调进行感知。同时,在开发过程中打开多线程检测开关,避免出现多线程安全问题。 +可参考[常见多线程安全问题](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-stability-ark-runtime-detection#section19357830121120) -3. 崩溃本身,该崩溃可能发生在调用napi_call_function时,入参 func 有问题,即非法入参,开发者可排查napi_value是否被缓存。这种情况可能是napi_value被缓存之后,napi_value不在作用域导致失效。 +3. 对于崩溃问题本身,该崩溃可能发生在调用napi_call_function时,入参 func 有问题,即非法入参,开发者可排查napi_value是否被缓存。这种情况可能是napi_value被缓存后,napi_value超出napi_scope作用域导致失效。 如果有类似逻辑,需使用napi_ref进行存储,napi_ref可以延长生命周期。 - 可参考文档: @@ -93,9 +92,9 @@ napi没有提供直接获取napi_env的能力,只能通过传递一层一层 ## napi_add_env_cleanup_hook调用报错该如何处理 --具体问题:napi_add_env_cleanup_hook/napi_remove_env_cleanup_hook调用报错,该如何处理? -`napi_add_env_cleanup_hook`/`napi_remove_env_cleanup_hook`调用报错,有以下几个常见原因和对应的特征日志,均为接口使用不当导致。 -1. 在`env`所在的js线程外使用上述两个接口,导致多线程安全问题。特征报错日志`current napi interface cannot run in multi-thread`。 +-具体问题:napi_add_env_cleanup_hook/napi_remove_env_cleanup_hook调用报错,该如何处理? +`napi_add_env_cleanup_hook` 和 `napi_remove_env_cleanup_hook` 调用报错,常见原因和特征日志如下,均为接口使用不当导致。 +1. 在`env`所在的ArkTS线程外使用上述两个接口,导致多线程安全问题。特征报错日志`current napi interface cannot run in multi-thread`。 2. 调用`napi_add_env_cleanup_hook`时,重复使用同一个`args`注册不同的回调函数,导致后续注册失败问题。该接口第三个入参`args`是作为接口内部`map`的`key`值,当重复注册同一个`args`的回调时,后续注册动作将会失败,仅第一次注册才会成功。注册失败可能导致后续业务功能异常或崩溃。特征报错日志`AddCleanupHook Failed`。 3. 调用`napi_remove_env_cleanup_hook`时,尝试通过一个不存在(或已被删除)的`args`删除回调函数,该接口调用失败,出现特征报错日志`RemoveCleanupHook Failed`。 @@ -120,9 +119,9 @@ static napi_value Test(napi_env env, napi_callback_info info) ``` - 修复建议: -1. 对于多线程安全问题,需确保调用接口的线程在`env`所在的js线程上。 +1. 对于多线程安全问题,需确保调用接口的线程在`env`所在的ArkTS线程上。 2. 对于注册失败的问题,需由使用者明确待注册的函数。需要保证`key`值(也就是`napi_add_env_cleanup_hook`的第三个入参)是唯一的即可。 -3. 对于删除失败的问题,需要使用者确保`args`之前被注册过且未被删除。 +3. 对于删除失败的问题,需要使用者确保`args`已注册过且未被删除。 相关参考资料链接: [使用Node-API接口注册和使用环境清理钩子](use-napi-about-cleanuphook.md) diff --git a/zh-cn/application-dev/napi/use-napi-faqs.md b/zh-cn/application-dev/napi/use-napi-faqs.md index 427e97cae4bdbad721e16d89139425e9296687c6..bd34fb39fb13284fb8967150285026b25832c879 100644 --- a/zh-cn/application-dev/napi/use-napi-faqs.md +++ b/zh-cn/application-dev/napi/use-napi-faqs.md @@ -2,32 +2,32 @@ ## 稳定性 1. [应用运行过程中出现高概率闪退,出现cppcrash栈,栈顶为系统库libark_jsruntime.so,崩溃栈前几帧也有libace_napi.z.so,怎么进行定位解决](napi-faq-about-stability.md#应用运行过程中出现高概率闪退怎么进行定位解决) -2. [c++线程池中并发调用ArkTS方法(c++多线程调用ArkTS方法),如何处理线程安全问题?](napi-faq-about-stability.md#线程池中并发调用arkts方法如何处理线程安全问题) +2. [c++线程池中并发调用ArkTS方法(c++多线程调用ArkTS方法),如何处理线程安全问题?](napi-faq-about-stability.md#线程池中并发调用ArkTS方法如何处理线程安全问题) 3. [【napi_value非预期】napi_value内容产生变化,napi_value创建时类型是napi_function,保存一段时间后napi_value类型发生变化](napi-faq-about-stability.md#napi_value内容产生变化) 4. [是否存在获取最新napi_env的方法?](napi-faq-about-stability.md#是否存在获取最新napi_env的方法) 5. [napi_add_env_cleanup_hook/napi_remove_env_cleanup_hook调用报错,该如何处理?](napi-faq-about-stability.md#napi_add_env_cleanup_hook调用报错该如何处理) ## 内存泄漏 1. [napi_create_reference可以创建对js对象的引用,保持js对象不释放,正常来说使用完需要使用napi_delete_reference进行释放,但怕漏delete导致js对象内存泄漏,请问当前是否有机制来检查/测试是否有泄漏的napi_reference?](napi-faq-about-memory-leak.md#请问当前是否有机制来检查是否有泄漏的napi_ref) -2. [napi开发过程中,遇见内存泄漏问题,要怎么定位解决?](napi-faq-about-memory-leak.md#napi开发过程中遇见内存泄漏问题要怎么定位解决) -3. [参数泄漏问题参考napi_open_handle_scope、napi_close_handle_scope](use-napi-life-cycle.md) -4. [napi_threadsafe_function内存泄漏,应该如何处理?](napi-faq-about-memory-leak.md#napi_threadsafe_function内存泄漏应该如何处理) +2. [Node-API开发过程中,遇见内存泄漏问题,要怎么定位解决?](napi-faq-about-memory-leak.md#napi开发过程中遇见内存泄漏问题要怎么定位解决) +3. [参数泄漏问题参考napi_open_handle_scope、napi_close_handle_scope](use-napi-life-cycle.md) +4. [napi_threadsafe_function内存泄漏,应该如何处理?](napi-faq-about-memory-leak.md#napi_threadsafe_function内存泄漏应该如何处理) ## 常见基本功能问题 -1. [模块加载失败,Error message: is not callable NativeModule调用报错?](napi-faq-about-common-basic.md) -2. [是否有保序的线程通信推荐写法?](napi-faq-about-common-basic.md#在大量需要调用arkts方法进行通信的场景如何保证异步任务的有序性) -3. [是否存在便捷的NAPI回调arkts的方式?](napi-faq-about-common-basic.md#是否存在便捷的回调arkts的方式) -4. [如何在C++调用从ArkTS传递过来的function?](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-ndk-26) -5. [如何在遵循 N-API 单一返回值约束的前提下,安全、高效地将多个返回值(包括结构化数据和指针信息)传递给 ArkTS 运行时环境,并确保数据类型的正确映射与内存管理的安全性?](napi-faq-about-common-basic.md#如何确保数据类型的正确映射与内存管理的安全性) -6. [napi调用三方so](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-dynamic-link-library) -7. [napi_get_uv_event_loop接口错误码说明](napi-faq-about-common-basic.md#napi_get_uv_event_loop接口错误码说明) -8. [【NAPI】native层调用ts层对象方法,必须传入一个function给native层吗?](napi-faq-about-common-basic.md#native层调用ts层对象方法必须传入一个function给native层吗) -9. [在c++通过pthread或std::thread创建的线程,是否能调用ets的方法并获取到结果?](napi-faq-about-common-basic.md#是否能调用ets的方法并获取到结果) -10. [当前napi的napi_get_value_string_utf8每次调用的时候都要进行拷贝,是否有nocopy、不拷贝的napi_get_value_string_utf8接口或者能力? ](napi-faq-about-common-basic.md#是否有不拷贝的napi_get_value_string_utf8接口或者能力) -11. [多线程下napi env的使用注意事项是什么?是否存在napi_env切换导致的异常问题?是否必须在主线程?](napi-faq-about-common-basic.md#多线程下napi_env的使用注意事项) -12. [请问napi_call_threadsafe_function执行顺序是怎样的?](napi-faq-about-common-basic.md#napi_call_threadsafe_function执行顺序不符合预期) -13. [ArkTS/JS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not callable或明确的Error message](napi-faq-about-common-basic.md#arkts侧报错显示undefined) +1. [模块加载失败,Error message: is not callable NativeModule调用报错?](napi-faq-about-common-basic.md) +2. [是否有保序的线程通信推荐写法?](napi-faq-about-common-basic.md#在大量需要调用ArkTS方法进行通信的场景中如何保证异步任务的有序性) +3. [是否存在便捷的NAPI回调ArkTS的方式?](napi-faq-about-common-basic.md#是否存在便捷的回调ArkTS的方式) +4. [如何在C++调用从ArkTS传递过来的function?](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-ndk-26) +5. [如何在遵循 Node-API 单一返回值约束的前提下,安全、高效地将多个返回值(包括结构化数据和指针信息)传递给 ArkTS 运行时环境,并确保数据类型的正确映射与内存管理的安全性?](napi-faq-about-common-basic.md#如何确保数据类型的正确映射与内存管理的安全性) +6. [Node-API调用三方so](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-dynamic-link-library) +7. [napi_get_uv_event_loop接口错误码说明](napi-faq-about-common-basic.md#napi_get_uv_event_loop接口错误码说明) +8. [Node-API中,native层调用ArkTS层对象方法,必须传入一个function给native层吗?](napi-faq-about-common-basic.md#native层调用ArkTS层对象方法必须传入一个function给native层吗) +9. [在c++通过pthread或std::thread创建的线程,是否能调用ets的方法并获取到结果?](napi-faq-about-common-basic.md#是否能调用ets的方法并获取到结果) +10. [当前napi的napi_get_value_string_utf8每次调用的时候都要进行拷贝,是否有nocopy、不拷贝的napi_get_value_string_utf8接口或者能力? ](napi-faq-about-common-basic.md#是否有不拷贝的napi_get_value_string_utf8接口或者能力) +11. [多线程下napi_env的使用注意事项是什么?是否存在napi_env切换导致的异常问题?是否必须在主线程?](napi-faq-about-common-basic.md#多线程下napi_env的使用注意事项) +12. [请问napi_call_threadsafe_function执行顺序是怎样的?](napi-faq-about-common-basic.md#napi_call_threadsafe_function执行顺序不符合预期) +13. [ArkTS侧import xxx from libxxx.so后,使用xxx报错显示undefined/not callable或明确的Error message](napi-faq-about-common-basic.md#ArkTS侧报错显示undefined) 14. [接口执行结果非预期,日志显示occur exception need return](napi-faq-about-common-basic.md#接口执行结果非预期) 15. [napi_value和napi_ref的生命周期有何区别](napi-faq-about-common-basic.md#napi_value和napi_ref的生命周期有何区别) -16. [Node-API接口返回值不是napi_ok时,如何排查定位](napi-faq-about-common-basic.md#node-api接口返回值不是napi_ok时如何排查定位) -17. [napi_wrap如何保证被wrap的对象按期望顺序析构?](napi-faq-about-common-basic.md#napi_wrap如何保证被wrap的对象按期望顺序析构) +16. [Node-API接口返回值不是napi_ok时,如何排查定位](napi-faq-about-common-basic.md#NAPI接口返回值不是napi_ok时如何排查定位) +17. [napi_wrap如何保证被wrap的对象按期望顺序析构?](napi-faq-about-common-basic.md#napi_wrap如何保证被wrap的对象按期望顺序析构) 18. [napi_call_threadsafe_function回调任务不执行](napi-faq-about-common-basic.md#napi_call_threadsafe_function回调任务不执行)