文档管理中心
我的
您当前正在浏览新版开发者文档中心,目录分类和层级有所调整。点击左侧当前文档分类名称前的“☰”图标,可切换文档分类。 了解新版目录
问题处理与运维开发与测试问题应用质量技术质量稳定性如何定位应用中Native层出现的空指针解引用CppCrash

如何定位应用中Native层出现的空指针解引用CppCrash

问题现象

长稳测试的时候,偶现CppCrash崩溃,每次的崩溃栈不同,复现的场景不明确。查看多个崩溃日志都有出现SIGSEGV(SEGV_MAPERR)@0x0000000000000000 probably caused by NULL pointer dereference报错,其中之一的报错信息如下:

收起
自动换行
深色代码主题
复制
  1. Generated by HiviewDFX@OpenHarmony
  2. ================================================================
  3. Device info:HUAWEI Mate 60 Pro
  4. Build info:ALN-AL00 5.0.0.150(SP8C00E150R4P30log)
  5. Fingerprint:xxxxxxxxx
  6. Module name:com.example.application
  7. Version:2.8.5
  8. VersionCode:30
  9. PreInstalled:No
  10. Foreground:No
  11. Timestamp:2025-04-30 14:17:03.643
  12. Pid:42378
  13. Uid:20020210
  14. Process name:com.example.application
  15. Process life time:18446744073709405886s
  16. Reason:Signal:SIGSEGV(SEGV_MAPERR)@0x0000000000000007 probably caused by NULL pointer dereference
  17. Fault thread info:
  18. Tid:42378, Name:m.example.application
  19. #00 pc 0000000000135dc8 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
  20. #01 pc 0000000000135d68 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
  21. #02 pc 00000000001397c0 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
  22. #03 pc 000000000012b0d8 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
  23. #04 pc 000000000012aa78 /data/storage/el1/bundle/libs/arm64/libnet.so(doCallback(napi_env__*, napi_status, void*)+1584)(91a0f067f914ed44654e1191fccc295e60882cb0)
  24. #05 pc 00000000000704f4 /system/lib64/platformsdk/libace_napi.z.so(NativeSafeAsyncWork::ProcessAsyncHandle()+596)(85e70f1f5cf0e81468ec141fc2f4922c)
  25. #06 pc 0000000000016de4 /system/lib64/platformsdk/libuv.so(uv__async_io+352)(81fe2b78ed1df8447a6c4e51c5f644e2)
  26. #07 pc 0000000000028840 /system/lib64/platformsdk/libuv.so(uv__io_poll+1016)(81fe2b78ed1df8447a6c4e51c5f644e2)
  27. #08 pc 00000000000173cc /system/lib64/platformsdk/libuv.so(uv_run+408)(81fe2b78ed1df8447a6c4e51c5f644e2)
  28. #09 pc 000000000007f250 /system/lib64/platformsdk/libruntime.z.so(OHOS::AbilityRuntime::OHOSLoopHandler::OnTriggered()+140)(17e438b05b67ccb9c7eb96e54ed833e5)
  29. #10 pc 0000000000020430 /system/lib64/chipset-pub-sdk/libeventhandler.z.so(770d0cd78402a5ad1ced37fad50c5707)
  30. #11 pc 000000000001bdb4 /system/lib64/chipset-pub-sdk/libeventhandler.z.so(OHOS::AppExecFwk::EventHandler::DistributeEvent(std::__h::unique_ptr<OHOS::AppExecFwk::InnerEvent, void (*)(OHOS::AppExecFwk::InnerEvent*)> const&)+1140)(770d0cd78402a5ad1ced37fad50c5707)

背景知识

CppCrash(进程崩溃)是指C/C++运行时崩溃。目前主要支持崩溃异常信号SIGBUS、SIGFPE、SIGSEGV、SIGSTKFLT、SIGSYS、SIGILL、SIGTRAP、SIGABRT的处理。可以通过FaultLogger模块获取崩溃日志。

崩溃日志报错中空指针解引用NULL pointer dereference表现形式一般为:

  • 形如SIGSEGV(SEGV_MAPERR)@0x00000000或CppCrash日志的Register中打印的r0,r1等传参寄存器的值为0时,应首先考虑检查调用时是否传入了空指针。
  • 形如SIGSEGV(SEGV_MAPERR)@0x0000000c或CppCrash日志Register中打印的r1等传参寄存器的值为一个很小的值时应考虑检查调用入参的结构体成员是否包含空指针。

定位该类型问题一般尝试以下几种方法:

  1. 使用DevEco Studio中链接应用,尝试直接点击崩溃日志中的调用栈跳转到对应行号。在应用开发场景,对于应用自身的动态库生成的CppCrash堆栈可直接跳转到代码行处,支持Native栈帧和ArkTS栈帧,无需开发者自行进行解析行号操作。
  2. 对于未能解析跳转到对应行号的栈帧,可使用llvm-addr2line工具解析定位崩溃行号。注意在使用llvm-addr2line工具前需要将release编译的so带上符号表,同时将build-profile.json5中debugSymbol的strip字段设置为"false"。
  3. 使用ASan检测内存错误。使用DevEco Studio集成的ASan(Address-Sanitizer)来检测面向C/C++的地址报错,并通过FaultLog展示的错误堆栈详情及导致错误的代码行来进行问题定位。
  4. 结合业务检视代码。根据基于崩溃栈定位行号章节中介绍的三种方式获取到栈顶对应的行号后,回到代码中,检视上下文。

问题定位

  1. 由于出现问题的场景不明确,所以首先通过崩溃栈日志进行规律总结。经过查看多次的崩溃栈,发现基本都是在回调函数中出现的崩溃,比如以下的libnet.so中doCallback函数,于是怀疑问题出在各回调函数中的共性问题。
    收起
    自动换行
    深色代码主题
    复制
    1. pc 0000000000135dc8 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
    2. #01 pc 0000000000135d68 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
    3. #02 pc 00000000001397c0 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
    4. #03 pc 000000000012b0d8 /data/storage/el1/bundle/libs/arm64/libnet.so(91a0f067f914ed44654e1191fccc295e60882cb0)
    5. #04 pc 000000000012aa78 /data/storage/el1/bundle/libs/arm64/libnet.so(doCallback(napi_env__*, napi_status, void*)+1584)(91a0f067f914ed44654e1191fccc295e60882cb0)
  2. 以doCallback回调作为突破口,尝试通过添加空指针保护,发现还是会偶现该问题。于是怀疑是由于doCallback函数在执行过程中被释放了使用的指针内存资源。
    收起
    自动换行
    深色代码主题
    复制
    1. void doCallback(napi_env env, napi_status status, void *data) {
    2. if (env == nullptr || data == nullptr) {
    3. LOGE("Invalid env or data");
    4. return;
    5. }
    6. // ...
    7. }
  3. 使用llvm-addr2line(或者堆栈轨迹分析)对出现崩溃的libnet.so尝试解析出文件名和行号。
    • 首先将libnet模块的build-profile.json5里面buildOption/externalNativeOptions中配置参数:"arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo"。
      收起
      自动换行
      深色代码主题
      复制
      1. {
      2. "apiType": "stageMode",
      3. "buildOption": {
      4. "externalNativeOptions": {
      5. "path": "./src/main/cpp/CMakeLists.txt",
      6. "arguments": ["-DCMAKE_BUILD_TYPE=RelWithDebInfo"],
      7. "cppFlags": "",
      8. }
      9. },
      10. // ...
      11. }
    • 然后将libnet模块的build-profile.json5里面buildOptionSet/nativeLib/debugSymbol/strip设置为"false"。(注意下方示例中的name为"debug"包,如需"release"包带上符号表则需要在"name"为"release"的构建配置中修改)。
      收起
      自动换行
      深色代码主题
      复制
      1. "buildOptionSet": [
      2. {
      3. "name": "debug",
      4. "arkOptions": {
      5. "obfuscation": {
      6. "ruleOptions": {
      7. "enable": false,
      8. "files": [
      9. "./obfuscation-rules.txt"
      10. ]
      11. }
      12. }
      13. },
      14. "nativeLib": {
      15. "debugSymbol": {
      16. "strip": false,
      17. "exclude": []
      18. }
      19. }
      20. },
      21. ],
    • 编译出libnet.so后,确认是否已带上符号表:
      • 在Linux中可使用file libnet.so命令,查看最后是否有not stripped字段,带了则表示编译出的libnet.so带上了符号表。
        收起
        自动换行
        深色代码主题
        复制
        1. [root@ncn4b-partnervectorservice-33-83-97 arm64-v8a]# file libnet.so
        2. libnet.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=9d075a569e007bc6e4ea25f2f96be548724b4aef, with debug_info, not stripped
      • 在windows中可使用llvm-objdump --headers libnet.so(llvm-objdump工具在DevEco Studio安装的时候会自带),查看是否有debug字段,有则说明带上了符号表,没有则说明无符号表:

    • 使用编译出来的带符号表的libnet.so使用llvm-addr2line进行崩溃行号定位:
      收起
      自动换行
      深色代码主题
      复制
      1. llvm-addr2line -Cipe LocalPath/libnet.so 000000000012aa78
    • 得到出问题的行信息,定位到崩溃在napi_call_function函数这行。
      收起
      自动换行
      深色代码主题
      复制
      1. napi_call_function(env, nullptr, callback, 1, &returnValue, &tempValue);

    说明是napi_call_function在调用ArkTS函数指针的时候,回调函数指针可能被其他线程释放了才导致的踩空。

  4. 通过代码分析发现问题所在ArkTS层往Native层传递函数参数的时候,需要依赖的回调函数指针必须是有效的。

    但是从上层传下来的函数制造者却没有创建强引用napi_ref保留。所以当GC回收了ArkTS的回调函数资源后,使用该ArkTS回调函数可能在Native执行napi_call_function前就已经是空指针了,最后导致空指针解引用。

分析结论

  • 使用napi_call_function函数调用ArkTS层函数前,要使用napi_create_reference创建一个强引用napi_ref,防止GC回收ArkTS的回调函数资源。
  • 然后通过napi_get_reference_value从napi_ref中提取当前有效的napi_value。此时napi_value就是持久化后的ArkTS层的持久化的函数指针,这个时候使用napi_call_function函数调用改指针才是安全的。
  • 在使用完后需要调用napi_delete_reference主动释放引用,避免内存泄漏。

修改建议

  • 在Native层中,首先在获取异步回调ArkTS层函数指针的时候,需要使用napi_create_reference创建强引用将指针缓存起来,在使用结束后,需要调用napi_delete_reference释放强引用,避免资源泄露。示例代码如下:
    收起
    自动换行
    深色代码主题
    复制
    1. // 异步工作Native流程
    2. static napi_value StartAsyncWork(napi_env env, napi_callback_info info)
    3. {
    4. // 从模块数据中获取函数引用
    5. ModuleData* moduleData = nullptr;
    6. napi_get_instance_data(env, (void**)&moduleData);
    7. if (moduleData == nullptr || moduleData->arktsFuncRef == nullptr) {
    8. napi_throw_error(env, nullptr, "请先注册ArkTS函数");
    9. return nullptr;
    10. }
    11. // 创建新的工作数据
    12. AsyncWorkData* workData = new AsyncWorkData();
    13. workData->env = env;
    14. // 为异步工作创建新的函数引用
    15. napi_value arktsFunc;
    16. if (napi_get_reference_value(env, moduleData->arktsFuncRef, &arktsFunc) != napi_ok) {
    17. delete workData;
    18. napi_throw_error(env, nullptr, "获取注册的函数失败");
    19. return nullptr;
    20. }
    21. // 创建新的引用给异步工作使用
    22. if (napi_create_reference(env, arktsFunc, 1, &workData->arktsFuncRef) != napi_ok) {
    23. delete workData;
    24. napi_throw_error(env, nullptr, "为异步工作创建函数引用失败");
    25. return nullptr;
    26. }
    27. // 创建异步工作
    28. napi_async_work asyncWork;
    29. napi_value workName;
    30. napi_create_string_utf8(env, "MyProc", NAPI_AUTO_LENGTH, &workName);
    31. if (napi_create_async_work(env, nullptr, workName, ExecuteWork, doCallback, workData, &asyncWork) != napi_ok) {
    32. napi_delete_reference(env, workData->arktsFuncRef);
    33. delete workData;
    34. napi_throw_error(env, nullptr, "创建异步工作失败");
    35. return nullptr;
    36. }
    37. // 排队执行异步工作
    38. if (napi_queue_async_work(env, asyncWork) != napi_ok) {
    39. napi_delete_async_work(env, asyncWork);
    40. napi_delete_reference(env, workData->arktsFuncRef);
    41. delete workData;
    42. napi_throw_error(env, nullptr, "排队异步工作失败");
    43. return nullptr;
    44. }
    45. OH_LOG_INFO(LOG_APP, "异步工作已启动");
    46. napi_value result;
    47. napi_get_undefined(env, &result);
    48. return result;
    49. }
  • 在执行完数据的处理后,napi_create_async_work函数会回调doCallback函数,在该函数中,在使用napi_call_function函数调用ArkTS层函数指针前,使用napi_get_reference_value获取缓存中的ArkTS函数指针。示例代码如下:
    收起
    自动换行
    深色代码主题
    复制
    1. // 异步工作完成回调(在主线程/JS线程中执行)
    2. void doCallback(napi_env env, napi_status status, void* data)
    3. {
    4. AsyncWorkData* workData = static_cast<AsyncWorkData*>(data);
    5. if (status != napi_ok || workData == nullptr) {
    6. OH_LOG_ERROR(LOG_APP, "异步工作完成回调出错");
    7. return;
    8. }
    9. // 安全地调用ArkTS函数
    10. if (workData->arktsFuncRef != nullptr) {
    11. napi_handle_scope scope;
    12. napi_status status;
    13. // 创建作用域
    14. if (napi_open_handle_scope(env, &scope) != napi_ok) {
    15. OH_LOG_ERROR(LOG_APP, "打开handle scope失败");
    16. // 清理工作数据
    17. if (workData->arktsFuncRef != nullptr) {
    18. napi_delete_reference(env, workData->arktsFuncRef);
    19. }
    20. delete workData;
    21. return;
    22. }
    23. napi_value arktsFunc;
    24. status = napi_get_reference_value(env, workData->arktsFuncRef, &arktsFunc);
    25. if (status == napi_ok && arktsFunc != nullptr) {
    26. // 创建要传递给ArkTS函数的参数,本例为字符串
    27. napi_value argv[1];
    28. napi_create_string_utf8(env, workData->resultMessage.c_str(), NAPI_AUTO_LENGTH, &argv[0]);
    29. // 调用ArkTS函数
    30. napi_value global;
    31. napi_get_global(env, &global);
    32. napi_value result;
    33. if (napi_call_function(env, global, arktsFunc, 1, argv, &result) == napi_ok) {
    34. OH_LOG_INFO(LOG_APP, "成功调用ArkTS回调函数,消息: %{public}s",
    35. workData->resultMessage.c_str());
    36. } else {
    37. OH_LOG_ERROR(LOG_APP, "调用ArkTS函数失败");
    38. }
    39. } else {
    40. OH_LOG_ERROR(LOG_APP, "获取ArkTS函数引用失败");
    41. }
    42. // 关闭作用域
    43. napi_close_handle_scope(env, scope);
    44. }
    45. }

完整示例参考如下:

  • entry/src/main/cpp/CMakeLists.txt:
    收起
    自动换行
    深色代码主题
    复制
    1. # the minimum version of CMake.
    2. cmake_minimum_required(VERSION 3.5.0)
    3. project(SQI202505101216105442012)
    4. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    5. if(DEFINED PACKAGE_FIND_FILE)
    6. include(${PACKAGE_FIND_FILE})
    7. endif()
    8. include_directories(${NATIVERENDER_ROOT_PATH}
    9. ${NATIVERENDER_ROOT_PATH}/include)
    10. add_library(entry SHARED napi_init.cpp)
    11. target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libuv.so)
  • entry/src/main/cpp/napi_init.cpp:
    收起
    自动换行
    深色代码主题
    复制
    1. /*
    2. * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
    3. */
    4. #include "napi/native_api.h"
    5. #include <thread>
    6. #include "hilog/log.h"
    7. #include <uv.h>
    8. #undef LOG_DOMAIN
    9. #undef LOG_TAG
    10. #define LOG_DOMAIN 0x3200 // 全局domain宏,标识业务领域
    11. #define LOG_TAG "MY_TAG" // 全局tag宏,标识模块日志tag
    12. // 异步工作数据结构
    13. struct AsyncWorkData {
    14. napi_env env = nullptr;
    15. napi_ref arktsFuncRef = nullptr; // 保存ArkTS函数的引用
    16. std::string resultMessage; // 要传递给ArkTS的结果
    17. };
    18. // 模块全局数据
    19. struct ModuleData {
    20. napi_ref arktsFuncRef = nullptr;
    21. };
    22. // 异步工作执行函数(在工作线程中执行)
    23. void ExecuteWork(napi_env env, void* data)
    24. {
    25. AsyncWorkData* workData = static_cast<AsyncWorkData*>(data);
    26. // 模拟一些耗时操作
    27. for (int i = 0; i < 3; i++) {
    28. OH_LOG_INFO(LOG_APP, "耗时异步流程执行中... %{public}d", i);
    29. // 模拟工作耗时操作
    30. std::this_thread::sleep_for(std::chrono::milliseconds(500));
    31. }
    32. // 准备要传递给ArkTS的消息
    33. workData->resultMessage = "来自Native层的异步完成消息,时间戳: " +
    34. std::to_string(uv_hrtime() / 1000000);
    35. }
    36. // 异步工作完成回调(在主线程/JS线程中执行)
    37. void doCallback(napi_env env, napi_status status, void* data)
    38. {
    39. AsyncWorkData* workData = static_cast<AsyncWorkData*>(data);
    40. if (status != napi_ok || workData == nullptr) {
    41. OH_LOG_ERROR(LOG_APP, "异步工作完成回调出错");
    42. return;
    43. }
    44. // 安全地调用ArkTS函数
    45. if (workData->arktsFuncRef != nullptr) {
    46. napi_handle_scope scope;
    47. napi_status status;
    48. // 创建作用域
    49. if (napi_open_handle_scope(env, &scope) != napi_ok) {
    50. OH_LOG_ERROR(LOG_APP, "打开handle scope失败");
    51. // 清理工作数据
    52. if (workData->arktsFuncRef != nullptr) {
    53. napi_delete_reference(env, workData->arktsFuncRef);
    54. }
    55. delete workData;
    56. return;
    57. }
    58. napi_value arktsFunc;
    59. status = napi_get_reference_value(env, workData->arktsFuncRef, &arktsFunc);
    60. if (status == napi_ok && arktsFunc != nullptr) {
    61. // 创建要传递给ArkTS函数的参数,本例为字符串
    62. napi_value argv[1];
    63. napi_create_string_utf8(env, workData->resultMessage.c_str(), NAPI_AUTO_LENGTH, &argv[0]);
    64. // 调用ArkTS函数
    65. napi_value global;
    66. napi_get_global(env, &global);
    67. napi_value result;
    68. if (napi_call_function(env, global, arktsFunc, 1, argv, &result) == napi_ok) {
    69. OH_LOG_INFO(LOG_APP, "成功调用ArkTS回调函数,消息: %{public}s",
    70. workData->resultMessage.c_str());
    71. } else {
    72. OH_LOG_ERROR(LOG_APP, "调用ArkTS函数失败");
    73. }
    74. } else {
    75. OH_LOG_ERROR(LOG_APP, "获取ArkTS函数引用失败");
    76. }
    77. // 关闭作用域
    78. napi_close_handle_scope(env, scope);
    79. }
    80. }
    81. // 注册ArkTS函数的Native方法
    82. static napi_value RegisterArkTSFunction(napi_env env, napi_callback_info info)
    83. {
    84. size_t argc = 1;
    85. napi_value args[1];
    86. napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    87. if (argc < 1) {
    88. napi_throw_error(env, nullptr, "需要传入ArkTS函数作为参数");
    89. return nullptr;
    90. }
    91. napi_valuetype valuetype;
    92. napi_typeof(env, args[0], &valuetype);
    93. if (valuetype != napi_function) {
    94. napi_throw_type_error(env, nullptr, "参数必须是一个函数");
    95. return nullptr;
    96. }
    97. // 获取模块数据
    98. ModuleData* moduleData = nullptr;
    99. napi_get_instance_data(env, (void**)&moduleData);
    100. if (moduleData == nullptr) {
    101. moduleData = new ModuleData();
    102. napi_set_instance_data(env, moduleData, [](napi_env env, void* data, void* hint) {
    103. ModuleData* moduleData = static_cast<ModuleData*>(data);
    104. if (moduleData && moduleData->arktsFuncRef) {
    105. napi_delete_reference(env, moduleData->arktsFuncRef);
    106. }
    107. delete moduleData;
    108. }, nullptr);
    109. } else {
    110. // 如果已经注册过函数,先清理旧的引用
    111. if (moduleData->arktsFuncRef != nullptr) {
    112. napi_delete_reference(env, moduleData->arktsFuncRef);
    113. moduleData->arktsFuncRef = nullptr;
    114. }
    115. }
    116. // 创建对ArkTS函数的强引用,防止被GC
    117. napi_status status = napi_create_reference(env, args[0], 1, &moduleData->arktsFuncRef);
    118. if (status != napi_ok) {
    119. napi_throw_error(env, nullptr, "创建函数引用失败");
    120. return nullptr;
    121. }
    122. OH_LOG_INFO(LOG_APP, "ArkTS函数注册成功");
    123. napi_value result;
    124. napi_get_undefined(env, &result);
    125. return result;
    126. }
    127. // 异步工作Native流程
    128. static napi_value StartAsyncWork(napi_env env, napi_callback_info info)
    129. {
    130. // 从模块数据中获取函数引用
    131. ModuleData* moduleData = nullptr;
    132. napi_get_instance_data(env, (void**)&moduleData);
    133. if (moduleData == nullptr || moduleData->arktsFuncRef == nullptr) {
    134. napi_throw_error(env, nullptr, "请先注册ArkTS函数");
    135. return nullptr;
    136. }
    137. // 创建新的工作数据
    138. AsyncWorkData* workData = new AsyncWorkData();
    139. workData->env = env;
    140. // 为异步工作创建新的函数引用
    141. napi_value arktsFunc;
    142. if (napi_get_reference_value(env, moduleData->arktsFuncRef, &arktsFunc) != napi_ok) {
    143. delete workData;
    144. napi_throw_error(env, nullptr, "获取注册的函数失败");
    145. return nullptr;
    146. }
    147. // 创建新的引用给异步工作使用
    148. if (napi_create_reference(env, arktsFunc, 1, &workData->arktsFuncRef) != napi_ok) {
    149. delete workData;
    150. napi_throw_error(env, nullptr, "为异步工作创建函数引用失败");
    151. return nullptr;
    152. }
    153. // 创建异步工作
    154. napi_async_work asyncWork;
    155. napi_value workName;
    156. napi_create_string_utf8(env, "MyProc", NAPI_AUTO_LENGTH, &workName);
    157. if (napi_create_async_work(env, nullptr, workName, ExecuteWork, doCallback, workData, &asyncWork) != napi_ok) {
    158. napi_delete_reference(env, workData->arktsFuncRef);
    159. delete workData;
    160. napi_throw_error(env, nullptr, "创建异步工作失败");
    161. return nullptr;
    162. }
    163. // 排队执行异步工作
    164. if (napi_queue_async_work(env, asyncWork) != napi_ok) {
    165. napi_delete_async_work(env, asyncWork);
    166. napi_delete_reference(env, workData->arktsFuncRef);
    167. delete workData;
    168. napi_throw_error(env, nullptr, "排队异步工作失败");
    169. return nullptr;
    170. }
    171. OH_LOG_INFO(LOG_APP, "异步工作已启动");
    172. napi_value result;
    173. napi_get_undefined(env, &result);
    174. return result;
    175. }
    176. EXTERN_C_START
    177. static napi_value Init(napi_env env, napi_value exports)
    178. {
    179. napi_property_descriptor desc[] = {
    180. { "registerArkTSFunction", nullptr, RegisterArkTSFunction, nullptr, nullptr, nullptr, napi_default, nullptr },
    181. { "startAsyncWork", nullptr, StartAsyncWork, nullptr, nullptr, nullptr, napi_default, nullptr }
    182. };
    183. napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    184. return exports;
    185. }
    186. EXTERN_C_END
    187. static napi_module demoModule = {
    188. .nm_version = 1,
    189. .nm_flags = 0,
    190. .nm_filename = nullptr,
    191. .nm_register_func = Init,
    192. .nm_modname = "entry",
    193. .nm_priv = ((void*)0),
    194. .reserved = { 0 },
    195. };
    196. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
    197. {
    198. napi_module_register(&demoModule);
    199. }
  • entry/src/main/cpp/types/libentry/Index.d.ts:
    收起
    自动换行
    深色代码主题
    复制
    1. export const registerArkTSFunction: (callback: (message: string) => void) => void;
    2. export const startAsyncWork: () => void;
  • entry/src/main/ets/pages/Index.ets:
    收起
    自动换行
    深色代码主题
    复制
    1. import testNapi from 'libentry.so';
    2. // 要传递给Native层的回调函数
    3. function myArkTSFunction(message: string): void {
    4. console.info('ArkTS收到Native消息: ' + message);
    5. }
    6. @Entry
    7. @Component
    8. struct Index {
    9. @State message: string = 'Hello World';
    10. aboutToAppear(): void {
    11. this.registerCallbackToNative();
    12. }
    13. build() {
    14. Row() {
    15. Column() {
    16. Text(this.message)
    17. .fontSize(20)
    18. .margin(10)
    19. Button('触发异步工作')
    20. .onClick(() => {
    21. this.triggerAsyncWork();
    22. this.message = '异步工作已触发';
    23. })
    24. .margin(10)
    25. }
    26. .width('100%');
    27. }
    28. .height('100%');
    29. }
    30. // 注册回调函数到Native层
    31. registerCallbackToNative(): void {
    32. try {
    33. testNapi.registerArkTSFunction(myArkTSFunction);
    34. console.info('ArkTS函数注册成功');
    35. } catch (error) {
    36. console.error('注册ArkTS函数失败: ' + error);
    37. }
    38. }
    39. // 触发Native层的异步工作
    40. triggerAsyncWork(): void {
    41. try {
    42. testNapi.startAsyncWork();
    43. console.info('已触发Native异步工作');
    44. } catch (error) {
    45. console.error('触发异步工作失败: ' + error);
    46. }
    47. }
    48. }