[HarmonyOS应用开发]使用NAPI实现C与ArkTS交互

打印 上一主题 下一主题

主题 573|帖子 573|积分 1719

使用Node-API实现跨语言交互



  • ArkTS侧:实现C++方法的结果返回,以及实现其回调函数。
  • Native侧:.cpp文件,实现结果返回函数、回调函数以及设置回调数据。必要提供注册lib库的名称,并在注册回调方法中定义接口的映射关系,即Native方法及对应的ArkTS接口名称等。
与NAPI开发的相关目录介绍



  • ntry > src > main > cpp > types:用于存放C++的API接口形貌文件。
  • entry > src > main > cpp > types > libentry > index.d.ts:形貌C++ API接口行为,如接口名、入参、返回参数等。
  • entry > src > main > cpp > types > libentry > oh-package.json5:设置.so三方包声明文件的入口及包名。
  • entry > src > main > cpp > CMakeLists.txt:C++源码编译设置文件,提供CMake构建脚本。
  • entry > src > main > cpp > xxx.cpp:定义C++ API接口的文件。
Native侧方法的实现



  • 1. 设置模块注册信息
ArkTS侧import native模块时,会加载其对应的so。加载so时,起首会调用napi_module_register方法,将模块注册到系统中,并调用模块初始化函数。
napi_module有两个关键属性:一个是.nm_register_func,定义模块初始化函数;另一个是.nm_modname,定义模块的名称,也就是ArkTS侧引入的so库的名称,模块系统会根据此名称来区分差别的so。
  1. // 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
  2. static napi_module testModule = {
  3.     .nm_version = 1,
  4.     .nm_flags = 0,
  5.     .nm_filename = nullptr,
  6.     .nm_register_func = Init,
  7.     .nm_modname = "entry",
  8.     .nm_priv = ((void*)0),
  9.     .reserved = { 0 },
  10. };
  11. // 加载so时,该函数会自动被调用,将上述testModule模块注册到系统中。
  12. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  13. {
  14.     napi_module_register(&testModule);
  15. }
复制代码


  • 2. 模块初始化
实现ArkTS接口与C++接口的绑定和映射。
  1. EXTERN_C_START
  2. static napi_value Init(napi_env env, napi_value exports)
  3. {
  4.     napi_property_descriptor desc[] = {
  5.         { "resultMsg", nullptr, resultMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
  6.         {"setOnCallBack", nullptr, setOnCallBack, nullptr, nullptr, nullptr, napi_default, nullptr},
  7.         {"nativeToTSCallBack", nullptr, nativeToTSCallBack, nullptr, nullptr, nullptr, napi_default, nullptr}
  8.     };
  9.     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  10.     return exports;
  11. }
  12. EXTERN_C_END
复制代码


  • 3. 在index.d.ts文件中,提供JS侧的接口方法。
  1. export const resultMsg: (a: number, b: number) => number;
  2. export const setOnCallBack: (cbFn: (progress: number) => void) => void;
  3. export const nativeToTSCallBack: (a: number) => void;
复制代码


  • 4. 在oh-package.json5文件中将index.d.ts与cpp文件关联起来。
  1. {
  2.   "name": "libentry.so",
  3.   "types": "./index.d.ts",
  4.   "version": "",
  5.   "description": "Please describe the basic information."
  6. }
复制代码


  • 5. 在CMakeLists.txt文件中设置CMake打包参数。
  1. # the minimum version of CMake.
  2. cmake_minimum_required(VERSION 3.4.1)
  3. project(TestC)
  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. # 添加名为entry的库
  11. add_library(entry SHARED napi_init.cpp)
  12. # 构建此可执行文件需要链接的库
  13. target_link_libraries(entry PUBLIC libace_napi.z.so)
复制代码
实现Native的结果返回



  • 1.Native侧的函数代码为:
  1. /**
  2. * 返回测试
  3. */
  4. static napi_value resultMsg(napi_env env, napi_callback_info info)
  5. {
  6.     size_t argc = 2;
  7.     napi_value args[2] = {nullptr};
  8.     napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
  9.     napi_valuetype valuetype0;
  10.     napi_typeof(env, args[0], &valuetype0);
  11.     napi_valuetype valuetype1;
  12.     napi_typeof(env, args[1], &valuetype1);
  13.     double value0;
  14.     napi_get_value_double(env, args[0], &value0);
  15.     double value1;
  16.     napi_get_value_double(env, args[1], &value1);
  17.     napi_value sum;
  18.     napi_create_double(env, value0 + value1, &sum);
  19.     return sum;
  20. }
复制代码


  • 2. ArkTS侧调用Native侧的结果返回函数“resultMsg”
  1. Button(this.resultMsg)
  2.   .fontSize(20)
  3.   .fontWeight(FontWeight.Bold)
  4.   .onClick(() => {
  5.         this.add += 1;
  6.         this.resultMsg = `C函数的结果返回,返回结果为:${testNapi.resultMsg(this.add, 3)}`
  7.   })
复制代码
实现Native的数据回调



  • 1.Native侧的设置回调函数代码为:
  1. /**
  2. * 设置回调方法
  3. */
  4. static napi_value setOnCallBack(napi_env env, napi_callback_info info) {
  5.     size_t argc = 1;
  6.     napi_value args[1] = {nullptr};
  7.     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  8.     asyncContext->env = env;
  9.     napi_create_reference(env, args[0], 1, &asyncContext->callbackRef);
  10.     return nullptr;
  11. }
复制代码


  • 2.Native侧的触发回调函数代码为:
  1. /**
  2. * 测试数据回调到TS端展现出来
  3. */
  4. static napi_value nativeToTSCallBack(napi_env env, napi_callback_info info) {
  5.     size_t argc = 1;
  6.     napi_value args[1] = {nullptr};
  7.     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  8.     napi_valuetype valuetype0;
  9.     napi_typeof(env, args[0], &valuetype0);
  10.     int value0;
  11.     napi_get_value_int32(env, args[0], &value0);
  12.    
  13.     asyncContext->progress = value0;
  14.     std::thread callBackThread(callBackTask);
  15.     callBackThread.detach();
  16.     return nullptr;
  17. }
复制代码


  • 3.ArkTS侧实现回调的代码:
  1. aboutToAppear(): void {
  2.     testNapi.setOnCallBack((data: number) => {
  3.       this.callBackMsg = `C函数的结果回调,回调结果为:${data}`
  4.     });
  5. }
复制代码


  • 4.ArkTS侧触发C++函数举行回调的代码:
  1. Button('函数回调测试')
  2.   .fontSize(20)
  3.   .fontWeight(FontWeight.Bold)
  4.   .onClick(() => {
  5.         testNapi.nativeToTSCallBack(1)
  6.   })
  7.   .margin({'top': 50})
复制代码
Native侧完备代码:

  1. #include "napi/native_api.h"#include <thread>#include <uv.h>/**
  2. * 返回测试
  3. */
  4. static napi_value resultMsg(napi_env env, napi_callback_info info)
  5. {
  6.     size_t argc = 2;
  7.     napi_value args[2] = {nullptr};
  8.     napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
  9.     napi_valuetype valuetype0;
  10.     napi_typeof(env, args[0], &valuetype0);
  11.     napi_valuetype valuetype1;
  12.     napi_typeof(env, args[1], &valuetype1);
  13.     double value0;
  14.     napi_get_value_double(env, args[0], &value0);
  15.     double value1;
  16.     napi_get_value_double(env, args[1], &value1);
  17.     napi_value sum;
  18.     napi_create_double(env, value0 + value1, &sum);
  19.     return sum;
  20. }typedef struct CallbackContext {    napi_env env = nullptr;    napi_ref callbackRef = nullptr;    int progress = 0;} CallbackContext;auto asyncContext = new CallbackContext();// 回调ts侧函数,将进度信息关照到ts侧static void callTS(napi_env env, napi_value jsCb, void *context, void *data) {    CallbackContext *arg = (CallbackContext *)data;    napi_value progress;    napi_create_int32(arg->env, arg->progress, &progress);    napi_call_function(arg->env, nullptr, jsCb, 1, &progress, nullptr);}void callBackTask() {    // 创建线程安全函数    napi_value workName;    napi_create_string_utf8(asyncContext->env, "callBack", NAPI_AUTO_LENGTH, &workName);    napi_value jsCb;    napi_get_reference_value(asyncContext->env, asyncContext->callbackRef, &jsCb);    napi_threadsafe_function tsfn;    napi_create_threadsafe_function(asyncContext->env, jsCb, nullptr, workName, 0, 1, nullptr, nullptr, nullptr, callTS,                                    &tsfn);    while (asyncContext && asyncContext->progress < 10) {        asyncContext->progress += 1;        napi_acquire_threadsafe_function(tsfn);        napi_call_threadsafe_function(tsfn, (void *)asyncContext, napi_tsfn_blocking);        std::this_thread::sleep_for(std::chrono::milliseconds(100));    }};/**
  21. * 设置回调方法
  22. */
  23. static napi_value setOnCallBack(napi_env env, napi_callback_info info) {
  24.     size_t argc = 1;
  25.     napi_value args[1] = {nullptr};
  26.     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  27.     asyncContext->env = env;
  28.     napi_create_reference(env, args[0], 1, &asyncContext->callbackRef);
  29.     return nullptr;
  30. }/**
  31. * 测试数据回调到TS端展现出来
  32. */
  33. static napi_value nativeToTSCallBack(napi_env env, napi_callback_info info) {
  34.     size_t argc = 1;
  35.     napi_value args[1] = {nullptr};
  36.     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
  37.     napi_valuetype valuetype0;
  38.     napi_typeof(env, args[0], &valuetype0);
  39.     int value0;
  40.     napi_get_value_int32(env, args[0], &value0);
  41.    
  42.     asyncContext->progress = value0;
  43.     std::thread callBackThread(callBackTask);
  44.     callBackThread.detach();
  45.     return nullptr;
  46. }EXTERN_C_START
  47. static napi_value Init(napi_env env, napi_value exports)
  48. {
  49.     napi_property_descriptor desc[] = {
  50.         { "resultMsg", nullptr, resultMsg, nullptr, nullptr, nullptr, napi_default, nullptr },
  51.         {"setOnCallBack", nullptr, setOnCallBack, nullptr, nullptr, nullptr, napi_default, nullptr},
  52.         {"nativeToTSCallBack", nullptr, nativeToTSCallBack, nullptr, nullptr, nullptr, napi_default, nullptr}
  53.     };
  54.     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  55.     return exports;
  56. }
  57. EXTERN_C_END// 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
  58. static napi_module testModule = {
  59.     .nm_version = 1,
  60.     .nm_flags = 0,
  61.     .nm_filename = nullptr,
  62.     .nm_register_func = Init,
  63.     .nm_modname = "entry",
  64.     .nm_priv = ((void*)0),
  65.     .reserved = { 0 },
  66. };
  67. // 加载so时,该函数会自动被调用,将上述testModule模块注册到系统中。
  68. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  69. {
  70.     napi_module_register(&testModule);
  71. }
复制代码
应用界面




参考:
使用Node-API实现跨语言交互开发流程-使用Node-API实现跨语言交互-代码开发-NDK开发-开发 | 华为开发者同盟 (huawei.com)



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表