复现概率高,每次崩溃栈略有区别,但是共性都是:崩溃栈顶是系统库的libark_jsruntime.so或者libace_napi.z.so。
Reason:Signal:SIGSEGV(SEGV_MAPERR)@0x00000136 probably caused by NULL pointer dereference Fault thread info: Tid:15894, Name:e.myapplication #00 pc 002b8dd4 /system/lib/platformsdk/libark_jsruntime.so #01 pc 0024d3e1 /system/lib/platformsdk/libark_jsruntime.so #02 pc 0024d0d9 /system/lib/platformsdk/libark_jsruntime.so #03 pc 002eac5d /system/lib/platformsdk/libark_jsruntime.so #04 pc 00428d0f /system/lib/platformsdk/libark_jsruntime.so
使用Node-API时如果出现高概率闪退,崩溃栈顶在系统库libark_jsruntime.so,一般是开发者Node-API接口使用不当导致。
排查是否存在多线程安全问题(概率较大)。
DevEco Studio中提供了相关开关,开启开关后,重新编译打包并运行,看看崩溃栈是不是符合下面这个文档的描述,如果是,那就是在使用Node-API时,存在多线程安全问题。
DevEco Studio开关:

使用Node-API接口时入参非法导致。
示例栈结构如下。
#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) -- Node-API的so,该位置显示具体调用报错的接口 #04 /data/storage/el1/bundle/libs/arm/libentry.so -- 你的so
a. 排查有没有napi_value未初始化,还没赋值成功,直接作为非法入参传递给接口了。
b. 排查有没有在这个易错API列表里面找到相应的篇章。
可参考文档:
对于问题一:
只能在C++线程中将ArkTS任务抛回ArkTS线程,这时候并不是同步调用,而是一个抛任务的动作。
需要注意的是,这个ArkTS方法真正的执行动作只能在ArkTS线程中完成,即,只能方法运行在对应的ArkTS线程上。
对于问题二:
上面提到,C++线程都是抛任务到ArkTS线程,进而执行ArkTS方法。关于线程安全,可参考使用Node-API接口进行线程安全开发。
另外,开发过程中也可以打开方舟多线程检测开关,这个开关可以拦截多线程安全问题。
具体描述:在一个程序初始化的时候,保存了env和一个method(napi_value),这个method在刚刚创建的时候有进行check,napi_typeof的结果是napi_function,符合预期。程序运行一段时间后,使用保存的env和method再去调用,发现method check不过了,此时不是一个napi_function了,保存与使用时均处于同一主线程,要如何解决?
排查建议:
确认是否napi_value出了scope还在使用,导致use-after-scope问题。
可参考文档:
保存时建议使用napi_ref,而不是直接保存napi_value。
#00 /system/lib/platformsdk/libark_jsruntime.so(panda::JSValueRef::IsFunction) #01 /system/lib/platformsdk/libace_napi.z.so(napi_call_function) #02 /data/storage/el1/bundle/libs/arm/libentry.so ...
关于保存napi_env:
Node-API没有提供直接获取napi_env的能力,只能通过逐层函数调用传递。一般不推荐保存napi_env,有两个原因:
其一,napi_env退出时候如果没有被使用方感知到,很容易出现use-after-free问题;
其二,napi_env和ArkTS线程是强绑定的,如果napi_env放在其它ArkTS线程使用,就会有多线程安全问题。
可参考文档:
该问题的关键在于:
如果要强行保存env,必须感知env是否退出,可以使用napi_add_env_cleanup_hook的回调进行感知。同时,在开发过程中打开多线程检测开关,避免出现多线程安全问题。
可参考文档:
对于崩溃问题本身,该崩溃可能发生在调用napi_call_function时,入参 func 有问题,即非法入参,开发者可排查napi_value是否被缓存。这种情况可能是napi_value被缓存后,napi_value超出napi_handle_scope作用域导致失效。
如果有类似逻辑,需使用napi_ref进行存储,napi_ref可以延长生命周期。
napi_add_env_cleanup_hook 和 napi_remove_env_cleanup_hook 调用报错,常见原因和特征日志如下,均为接口使用不当导致。
常见错误场景示例如下:
void AddEnvCleanupHook(napi_env env)
{
napi_add_env_cleanup_hook(env, [](void* args) -> void {
// cleanup function回调
}, env); // env是个通用的数据,即使此处没有重复注册,可能会被其它地方所提前注册,导致该处注册失败。
}
static napi_value Test(napi_env env, napi_callback_info info)
{
// 第一次注册
AddEnvCleanupHook(env);
// 第二次重复注册
AddEnvCleanupHook(env);
return nullptr;
} 相关参考资料链接:
napi_open_handle_scope 和 napi_close_handle_scope 调用出现稳定性问题,常见原因如下,均为接口使用不当导致。
常见错误场景示例如下:
#include "napi/native_api.h"
#include <hilog/log.h>
// 1. 全局Scope
static napi_handle_scope g_globalScope = nullptr;
static napi_value CallFunction(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1] = {nullptr};
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
napi_valuetype type = napi_undefined;
if (argv[0] == nullptr || napi_typeof(env, argv[0], &type) != napi_ok || type != napi_function) {
OH_LOG_INFO(LOG_APP, "JS函数参数非法");
napi_value errRet = nullptr;
napi_create_int32(env, -1, &errRet);
return errRet;
}
if (!g_globalScope) {
OH_LOG_INFO(LOG_APP, "【首次调用】全局Scope为空,执行open");
napi_open_handle_scope(env, &g_globalScope);
// 首次调用:执行JS函数
napi_value global = nullptr;
napi_get_global(env, &global);
napi_value result = nullptr;
napi_call_function(env, global, argv[0], argc, argv, &result);
return result; // 首次调用直接返回,不执行后续close逻辑
} else {
// 重入调用:直接返回固定值 + 关闭Scope
napi_value result = nullptr;
napi_create_int32(env, 10, &result);
OH_LOG_INFO(LOG_APP, "【重入调用】全局Scope非空,执行close");
napi_close_handle_scope(env, g_globalScope);
g_globalScope = nullptr;
return result;
}
} 接口声明
// index.d.ts export const callFunction : (func : Function) => void;
ArkTS侧示例代码
import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';
function reenterFunc(count = 1) : void{
hilog.info(0x0000, 'testTag', `【JS侧】递归`);
if (count <= 0) {
return;
}
testNapi.callFunction(() => reenterFunc(count - 1));
hilog.info(0x0000, 'testTag', `【JS侧】重入调用`);
return;
}
try {
testNapi.callFunction(reenterFunc);
hilog.info(0x0000, 'testTag', '【执行完成】');
} catch (error) {
hilog.error(0x0000, 'testTag', `调用错误:${error.message}`);
} CMakeLists.txt
cmake_minimum_required(VERSION 3.5.0)
project(Test)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp)
add_definitions( "-DLOG_DOMAIN=0xd0d0" )
add_definitions( "-DLOG_TAG=\"testTag\"" )
target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so) 相关参考资料链接: