适用流程:已有一些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
- cmake_minimum_required(VERSION 3.5.0)
- project(net)
- # SKIP REPATH SETTING. Next four lines are set for skipping repath.
- # The fourth line works. Don't know if the third line works.
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s -ftrapv -D_FORTIFY_SOURCE=2 -O2")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -ftrapv -D_FORTIFY_SOURCE=2 -O2")
- set(CMAKE_SKIP_BUILD_RPATH_TRUE)
- set(CMAKE_SKIP_RPATH TRUE)
- # SKIP REPATH SETTING. Next two line is set for skipping repath.
- add_compile_options(-fstack-protector-all)
- # stack protector and Strip
- add_link_options(-s)
- include_directories(${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/include)
- # 链接静态库(例如需要链接OpenSSL)
- link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/lib/${OHOS_ARCH}/libcrypto.a)
- link_libraries(${CMAKE_CURRENT_SOURCE_DIR}/lib/${OHOS_ARCH}/libssl.a)
- # 将所有.c文件添加进来
- file(GLOB_RECURSE SOURCES "./src/*.c")
- # 编译为动态库,net是动态库的名称
- # SHARED 动态库, STATIC 静态库
- add_library(net
- SHARED
- ${SOURCES})
- # 包含所有头文件的目录
- target_include_directories(net PUBLIC
- "${CMAKE_CURRENT_SOURCE_DIR}/include"
- "${CMAKE_CURRENT_SOURCE_DIR}/include/openssl"
- )
- # 添加导入库的查找目录
- target_link_directories(net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/${OHOS_ARCH}/)
- # 链接导入库
- target_link_libraries(net PUBLIC libssl.a libcrypto.a libace_napi.z.so libhilog_ndk.z.so libnet_connection.so)
复制代码 编写脚本(Windows)
以编译脚本(.bat)与CMakeLists在同级目录为例
- rd /s /Q build_cmake
- mkdir build_cmake
- cd build_cmake
- 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
- cmake --build .
- cd /d %~dp0
复制代码 编译
在命令行工具实行该脚本,即可打包成arm64-v8a的动态库,在build_cmake目录下获取libtest.so
使用NAPI调用动态库的函数
添加so
创建Native C++ Project
把动态库放在module的libs目录下
在napi.cpp中调用动态库的函数
假设C库中有一个函数是这样的:(必要ohos.permission.GET_NETWORK_INFO权限)
- int get_client_ip(char*** client_ip, int* ip_size);
复制代码 下面是napi.cpp中调用该函数
- #include "aaa.h"//为了方便使用C库里面的一些结构体和宏定义,把头文件拷贝过来并引用
- #include "dlfcn.h"
- #undef LOG_TAG
- #define LOG_TAG "NapiTest"
- #define NATIVE_PATH "/data/storage/el1/bundle/libs/arm64/libnet.so"//这个路径可以在上层通过context获取,目前是固定的
- void *native_lib = nullptr;
- #define GET_NAPI_ERROR() \
- const napi_extended_error_info *errorInfo; \
- status = napi_get_last_error_info(gEnv, &errorInfo); \
- if (status == napi_ok) { \
- OH_LOG_ERROR(LOG_APP, "last error info : %{public}s", errorInfo->error_message); \
- } else { \
- OH_LOG_ERROR(LOG_APP, "get last error error"); \
- }
- typedef int (*GetClientIP)(char ***client_ip, int *ip_size);
- static napi_value getClientIP(napi_env env, napi_callback_info info) {
- OH_LOG_INFO(LOG_APP, "getClientIP");
- char **client_ip = nullptr;
- int ip_size = 0;
- napi_value result = nullptr;
- if (native_lib == nullptr) {
- OH_LOG_INFO(LOG_APP, "arrayGetClientPublicIP native_lib is empty, dlopen");
- native_lib = dlopen(NATIVE_PATH, RTLD_LAZY | RTLD_GLOBAL);
- if (native_lib == nullptr) {
- OH_LOG_ERROR(LOG_APP, "cannot load library: %{public}s", dlerror());
- napi_get_undefined(env, &result);
- return result;
- }
- }
- dlerror();
- GetClientIP getIP = (GetClientIP)dlsym(native_lib, "get_client_ip");
- if (dlerror()) {
- OH_LOG_ERROR(LOG_APP, "load get_client_ip failed: %{public}s", dlerror());
- napi_get_undefined(env, &result);
- return result;
- }
- int value = getIP(&client_ip, &ip_size);
- if (value == ERR_SUCCESS) {
- OH_LOG_INFO(LOG_APP, "get client ip success, ip_size=%{public}d", ip_size);
- napi_value ip_array;
- napi_create_array_with_length(env, ip_size, &ip_array);
- for (int i = 0; i < ip_size; ++i) {
- OH_LOG_INFO(LOG_APP, "client ip[%{public}d]=%{public}s", i, client_ip[i]);
- napi_value ip_string;
- napi_create_string_utf8(env, client_ip[i], NAPI_AUTO_LENGTH, &ip_string);
- napi_set_element(env, ip_array, i, ip_string);
- }
- result = ip_array;
- } else {
- OH_LOG_INFO(LOG_APP, "get client ip failed, ret=%{public}d", value);
- napi_get_undefined(env, &result);
- }
- for (int i = 0; i < ip_size; ++i) {
- free(client_ip[i]);
- }
- free(client_ip);
- return result;
- }
- EXTERN_C_START
- static napi_value Init(napi_env env, napi_value exports)
- {
- napi_property_descriptor desc[] = {
- { "getClientIP", nullptr, getClientIP, nullptr, nullptr, nullptr, napi_default, nullptr }
- };
- napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
- return exports;
- }
- EXTERN_C_END
- static napi_module demoModule = {
- .nm_version = 1,
- .nm_flags = 0,
- .nm_filename = nullptr,
- .nm_register_func = Init,
- .nm_modname = "entry",
- .nm_priv = ((void*)0),
- .reserved = { 0 },
- };
- extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
- {
- napi_module_register(&demoModule);
- }
复制代码 CMakeLists.txt
把napi.cpp和头文件、库添加进来
- cmake_minimum_required(VERSION 3.5.0)
- project(NativeDemo)
- 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)
- link_libraries(${NATIVERENDER_ROOT_PATH}/../../../libs/${OHOS_ARCH}/libnet.so)
- add_library(entry SHARED napi.cpp)
- target_link_libraries(extry PUBLIC libace_napi.z.so libhilog_ndk.z.so libnet.so libnet_connection.so)
复制代码 桥接TS与C++
在Index.d.ts中添加该接口的TS界说
- export const getClientIP: () => Array<string>
复制代码 TS/ArkTS调用
终极,在TS代码中添加库的引用,即可调用该native接口
- import testNapi from 'libentry.so';
- @Entry
- @Component
- struct Index {
- @State message: string = ''
- build() {
- Row() {
- Column() {
- Button('Get Client IP')
- .fontSize(20)
- .onClick(() => {
- this.message = testNapi.getClientIP().toString()
- })
- Text(this.message)
- .fontSize(20)
- .margin({ top: 20 })
- }
- .width('100%')
- }
- .height('100%')
- }
- }
复制代码 签名,运行,大功告成!
Gitee代码库
https://gitee.com/huyuhy/harmony-os
先写这么多,回调下次再写。。。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |