IT评测·应用市场-qidao123.com技术社区

标题: HarmonyOS NEXT开辟:C代码打包so库,napi调用函数及回调 [打印本页]

作者: 商道如狼道    时间: 2024-11-12 10:02
标题: HarmonyOS NEXT开辟:C代码打包so库,napi调用函数及回调
适用流程:已有一些C代码,不想把源码添加到HarmonyOS工程中,想要把已有的C代码编译成HarmonyOS平台可以使用的so库,在DevEco Studio工程的cpp模块加载so库并调用里面的函数,终极使用ArkTS/TS调用该函数(获取回调)。
C源码可以添加到HarmonyOS工程的,可以省略手动编译过程。

  
C代码打包.so动态库

获取OpenHarmony SDK

官网或套件货架获取DevEco Studio安装包(也可以直接下载Command Line Tools)
解压后得到 免安装版 或 exe(需安装)
安装后,在安装目录下找到 sdk\HarmonyOS-NEXT-DB1\openharmony\native
不同版本的路径名称略有差异,大抵雷同,终极找到native目录
配置环境变量

新建以下环境变量(假设你的SDK放在XXX目录下)
环境变量名环境变量值HDC_SERVER_PORT7035OHOS_HDC_SERVER_PORT7036OHOS_NDK_HOMEXXX\sdk\HarmonyOS-NEXT-DB1\openharmony\native 把以下路径加入Path环境变量
XXX\sdk\HarmonyOS-NEXT-DB1\openharmony\native\build-tools\cmake\bin
编写CMakeLists

在要编译的C代码根目录下添加CMakeLists.txt
  1. cmake_minimum_required(VERSION 3.5.0)
  2. project(net)
  3. # SKIP REPATH SETTING. Next four lines are set for skipping repath.
  4. # The fourth line works. Don't know if the third line works.
  5. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s -ftrapv -D_FORTIFY_SOURCE=2 -O2")
  6. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -ftrapv -D_FORTIFY_SOURCE=2 -O2")
  7. set(CMAKE_SKIP_BUILD_RPATH_TRUE)
  8. set(CMAKE_SKIP_RPATH TRUE)
  9. # SKIP REPATH SETTING. Next two line is set for skipping repath.
  10. add_compile_options(-fstack-protector-all)
  11. # stack protector and Strip
  12. add_link_options(-s)
  13. include_directories(${CMAKE_CURRENT_SOURCE_DIR}
  14.                     ${CMAKE_CURRENT_SOURCE_DIR}/include)
  15. # 链接静态库(例如需要链接OpenSSL)
  16. link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/lib/${OHOS_ARCH}/libcrypto.a)
  17. link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/lib/${OHOS_ARCH}/libssl.a)
  18. # 将所有.c文件添加进来
  19. file(GLOB_RECURSE SOURCES "./src/*.c")
  20. # 编译为动态库,net是动态库的名称
  21. # SHARED 动态库, STATIC 静态库
  22. add_library(net
  23.             SHARED
  24.             ${SOURCES})
  25. # 包含所有头文件的目录
  26. target_include_directories(net PUBLIC
  27.                                         "${CMAKE_CURRENT_SOURCE_DIR}/include"
  28.                                         "${CMAKE_CURRENT_SOURCE_DIR}/include/openssl"
  29. )
  30. # 添加导入库的查找目录
  31. target_link_directories(net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/${OHOS_ARCH}/)
  32. # 链接导入库
  33. target_link_libraries(net PUBLIC libssl.a libcrypto.a libace_napi.z.so libhilog_ndk.z.so libnet_connection.so)
复制代码
编写脚本(Windows)

以编译脚本(.bat)与CMakeLists在同级目录为例
  1. rd /s /Q build_cmake
  2. mkdir build_cmake
  3. cd build_cmake
  4. cmake -DCMAKE_C_COMPILER:FILEPATH=%OHOS_NDK_HOME%\llvm\bin\clang.exe -DCMAKE_CXX_COMPILER:FILEPATH=%OHOS_NDK_HOME%\llvm\bin\clang++.exe -DCMAKE_TOOLCHAIN_FILE=%OHOS_NDK_HOME%\build\cmake\ohos.toolchain.cmake -DCMAKE_MAKE_PROGRAM=%OHOS_NDK_HOME%\build-tools\cmake\bin\ninja.exe -DOHOS_ARCH=arm64-v8a -DOHOS_STL=c++_shared .. -G Ninja
  5. cmake --build .
  6. cd /d %~dp0
复制代码
编译

在命令行工具实行该脚本,即可打包成arm64-v8a的动态库,在build_cmake目录下获取libtest.so
使用NAPI调用动态库的函数

添加so

创建Native C++ Project

把动态库放在module的libs目录下

在napi.cpp中调用动态库的函数

假设C库中有一个函数是这样的:(必要ohos.permission.GET_NETWORK_INFO权限)
  1. int get_client_ip(char*** client_ip, int* ip_size);
复制代码
下面是napi.cpp中调用该函数
  1. #include "aaa.h"//为了方便使用C库里面的一些结构体和宏定义,把头文件拷贝过来并引用
  2. #include "dlfcn.h"
  3. #undef LOG_TAG
  4. #define LOG_TAG "NapiTest"
  5. #define NATIVE_PATH "/data/storage/el1/bundle/libs/arm64/libnet.so"//这个路径可以在上层通过context获取,目前是固定的
  6. void *native_lib = nullptr;
  7. #define GET_NAPI_ERROR()                                                                                                    \
  8.     const napi_extended_error_info *errorInfo;                                                                              \
  9.     status = napi_get_last_error_info(gEnv, &errorInfo);                                                                    \
  10.     if (status == napi_ok) {                                                                                                \
  11.         OH_LOG_ERROR(LOG_APP, "last error info : %{public}s", errorInfo->error_message);                                    \
  12.     } else {                                                                                                                \
  13.         OH_LOG_ERROR(LOG_APP, "get last error error");                                                                      \
  14.     }
  15. typedef int (*GetClientIP)(char ***client_ip, int *ip_size);
  16. static napi_value getClientIP(napi_env env, napi_callback_info info) {
  17.     OH_LOG_INFO(LOG_APP, "getClientIP");
  18.     char **client_ip = nullptr;
  19.     int ip_size = 0;
  20.     napi_value result = nullptr;
  21.     if (native_lib == nullptr) {
  22.         OH_LOG_INFO(LOG_APP, "arrayGetClientPublicIP native_lib is empty, dlopen");
  23.         native_lib = dlopen(NATIVE_PATH, RTLD_LAZY | RTLD_GLOBAL);
  24.         if (native_lib == nullptr) {
  25.             OH_LOG_ERROR(LOG_APP, "cannot load library: %{public}s", dlerror());
  26.             napi_get_undefined(env, &result);
  27.             return result;
  28.         }
  29.     }
  30.     dlerror();
  31.     GetClientIP getIP = (GetClientIP)dlsym(native_lib, "get_client_ip");
  32.     if (dlerror()) {
  33.         OH_LOG_ERROR(LOG_APP, "load get_client_ip failed: %{public}s", dlerror());
  34.         napi_get_undefined(env, &result);
  35.         return result;
  36.     }
  37.     int value = getIP(&client_ip, &ip_size);
  38.     if (value == ERR_SUCCESS) {
  39.         OH_LOG_INFO(LOG_APP, "get client ip success, ip_size=%{public}d", ip_size);
  40.         napi_value ip_array;
  41.         napi_create_array_with_length(env, ip_size, &ip_array);
  42.         for (int i = 0; i < ip_size; ++i) {
  43.             OH_LOG_INFO(LOG_APP, "client ip[%{public}d]=%{public}s", i, client_ip[i]);
  44.             napi_value ip_string;
  45.             napi_create_string_utf8(env, client_ip[i], NAPI_AUTO_LENGTH, &ip_string);
  46.             napi_set_element(env, ip_array, i, ip_string);
  47.         }
  48.         result = ip_array;
  49.     } else {
  50.         OH_LOG_INFO(LOG_APP, "get client ip failed, ret=%{public}d", value);
  51.         napi_get_undefined(env, &result);
  52.     }
  53.     for (int i = 0; i < ip_size; ++i) {
  54.         free(client_ip[i]);
  55.     }
  56.     free(client_ip);
  57.     return result;
  58. }
  59. EXTERN_C_START
  60. static napi_value Init(napi_env env, napi_value exports)
  61. {
  62.     napi_property_descriptor desc[] = {
  63.         { "getClientIP", nullptr, getClientIP, nullptr, nullptr, nullptr, napi_default, nullptr }
  64.     };
  65.     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  66.     return exports;
  67. }
  68. EXTERN_C_END
  69. static napi_module demoModule = {
  70.     .nm_version = 1,
  71.     .nm_flags = 0,
  72.     .nm_filename = nullptr,
  73.     .nm_register_func = Init,
  74.     .nm_modname = "entry",
  75.     .nm_priv = ((void*)0),
  76.     .reserved = { 0 },
  77. };
  78. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  79. {
  80.     napi_module_register(&demoModule);
  81. }
复制代码
CMakeLists.txt

把napi.cpp和头文件、库添加进来
  1. cmake_minimum_required(VERSION 3.5.0)
  2. project(NativeDemo)
  3. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
  4. if(DEFINED PACKAGE_FIND_FILE)
  5.     include(${PACKAGE_FIND_FILE})
  6. endif()
  7. include_directories(${NATIVERENDER_ROOT_PATH}
  8.                     ${NATIVERENDER_ROOT_PATH}/include)
  9. link_libraries(${NATIVERENDER_ROOT_PATH}/../../../libs/${OHOS_ARCH}/libnet.so)
  10. add_library(entry SHARED napi.cpp)
  11. target_link_libraries(extry PUBLIC libace_napi.z.so libhilog_ndk.z.so libnet.so libnet_connection.so)
复制代码
桥接TS与C++


在Index.d.ts中添加该接口的TS界说
  1. export const getClientIP: () => Array<string>
复制代码
TS/ArkTS调用

终极,在TS代码中添加库的引用,即可调用该native接口
  1. import testNapi from 'libentry.so';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   @State message: string = ''
  6.   build() {
  7.     Row() {
  8.       Column() {
  9.         Button('Get Client IP')
  10.           .fontSize(20)
  11.           .onClick(() => {
  12.             this.message = testNapi.getClientIP().toString()
  13.           })
  14.         Text(this.message)
  15.           .fontSize(20)
  16.           .margin({ top: 20 })
  17.       }
  18.       .width('100%')
  19.     }
  20.     .height('100%')
  21.   }
  22. }
复制代码
  签名,运行,大功告成!
  Gitee代码库

https://gitee.com/huyuhy/harmony-os
先写这么多,回调下次再写。。。


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




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4