C/C++三方库移植到HarmonyOS平台详细教程(补充版so库和头文件形式)
接上篇文章,上篇介绍了在有源码的情况下的编译。更多时候三方库的移植,可能只提供给你了arm64平台下的so库和头文件。那么如何移植到harmonyOS平台下呢?总结一句话就是利用so库和头文件,再封装成arkts侧能用的arkts接口,使用napi或者aki的方式。
📋 目录
- 移植概述
- 移植方式选择
- 源码方式移植
- 预编译so库方式移植
- 构建配置
- 实际案例
- 最佳实践
- 常见问题
预编译so库方式移植
在实际项目中,我们经常需要将C/C++库先编译为so库,然后提供头文件的方式给其他项目使用。这种方式有以下优势:
- 模块化: 将不同功能模块化,提升代码复用性
- 团队协作: 不同团队可以独立开发和维护
- 版本管理: 可以独立管理库的版本
- 编译效率: 避免重复编译,提高构建效率
预编译so库的两种集成方式
方式一:Native侧引用三方so库
这种方式适用于需要在Native侧调用so库功能的场景。
方案A:通过编译动态链接库的方式引用
实现原理: 将so库加入到工程中,在Native侧使用CMake编译动态链接库,通过include引用头文件调用功能函数。
开发步骤:
- 准备so库和头文件
# 项目结构
your_project/
├── entry/
│ ├── libs/
│ │ ├── arm64/
│ │ │ └── libyour_library.so
│ │ └── x86_64/
│ │ └── libyour_library.so
│ └── src/
│ └── main/
│ ├── cpp/
│ │ ├── your_library.h # 头文件
│ │ ├── napi_init.cpp # Node-API包装器
│ │ └── CMakeLists.txt
│ └── ets/
│ └── pages/
│ └── Index.ets
- 配置CMakeLists.txt
# entry/src/main/cpp/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(YourLibrary)set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})include_directories(${NATIVERENDER_ROOT_PATH}${NATIVERENDER_ROOT_PATH}/include)# 添加名为entry的库
add_library(entry SHARED napi_init.cpp)# 链接预编译的so库
target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/../../../libs/${OHOS_ARCH}/libyour_library.solibace_napi.z.so
)
- 实现Node-API包装器
为什么要再包装一层?因为c的so库,最终我们是想在arkts层使用的。因此需要包装一下,按照napi的接口规范,实现c接口到arkts接口的转换。
// entry/src/main/cpp/napi_init.cpp
#include <napi.h>
#include "your_library.h" // 包含预编译库的头文件
#include <string>// 包装预编译库的函数
static napi_value YourFunction(napi_env env, napi_callback_info info)
{size_t argc = 1;napi_value args[1] = {nullptr};// 获取参数napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 参数验证if (argc < 1) {napi_throw_error(env, nullptr, "需要至少1个参数");return nullptr;}// 获取字符串参数char input[256];size_t input_len;napi_get_value_string_utf8(env, args[0], input, sizeof(input), &input_len);// 调用预编译库的函数std::string result = your_library_function(input);// 返回结果napi_value result_value;napi_create_string_utf8(env, result.c_str(), NAPI_AUTO_LENGTH, &result_value);return result_value;
}// 模块初始化
static napi_value Init(napi_env env, napi_value exports) {napi_property_descriptor desc[] = {{"yourFunction", nullptr, YourFunction, nullptr, nullptr, nullptr, napi_default, nullptr}};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;
}// 模块注册
static napi_module yourModule = {.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 RegisterYourModule() { napi_module_register(&yourModule);
}
- 创建TypeScript类型定义
// entry/src/main/cpp/types/libentry/index.d.ts
export const yourFunction: (input: string) => string;
- 在ArkTS中使用
// entry/src/main/ets/pages/Index.ets
import nativeModule from 'libentry.so'@Entry
@Component
struct Index {@State result: string = '';build() {Row() {Column() {Text('调用预编译库').fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {this.result = nativeModule.yourFunction('Hello from ArkTS!');})Text(this.result).fontSize(30)}.width('100%')}.height('100%')}
}
方案B:通过调用dlopen的方式引用
实现原理: 将so库加入到工程中,在ArkTS侧将so库的沙箱路径传递至Native侧,在Native侧使用dlopen解析so库调用功能函数。
注意事项: 该方案只能引用C语言编译模式生成的so库,因此用于生成so库的.h头文件需要用extern “C” {}包裹。
开发步骤:
- 准备C语言接口的so库
// your_library.h (C语言接口)
#ifndef YOUR_LIBRARY_H
#define YOUR_LIBRARY_H#ifdef __cplusplus
extern "C" {
#endif// 导出函数声明
double add(double a, double b);
double sub(double a, double b);
const char* process_string(const char* input);#ifdef __cplusplus
}
#endif#endif // YOUR_LIBRARY_H
- 在ArkTS侧传递so库路径
// entry/src/main/ets/pages/Index.ets
import nativeModule from 'libentry.so'@Entry
@Component
struct Index {@State result: string = '';build() {Row() {Column() {Text('调用dlopen方式').fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {// 获取so库的沙箱路径let projectPath = this.getUIContext().getHostContext()!.bundleCodeDir;let abiPath = deviceInfo.abiList === 'x86_64' ? 'x86_64' : 'arm64';let soLibPath = `${projectPath}/libs/${abiPath}/libyour_library.so`;// 调用Native函数this.result = nativeModule.processWithDlopen('Hello', soLibPath);})Text(this.result).fontSize(30)}.width('100%')}.height('100%')}
}
- 实现dlopen方式的Node-API包装器
// entry/src/main/cpp/napi_init.cpp
#include <napi.h>
#include <dlfcn.h>
#include <cstring>// 定义函数指针类型
typedef const char* (*ProcessStringFunc)(const char*);static napi_value ProcessWithDlopen(napi_env env, napi_callback_info info)
{size_t argc = 2;napi_value args[2] = {nullptr};// 获取参数napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 获取输入字符串char input[256];size_t input_len;napi_get_value_string_utf8(env, args[0], input, sizeof(input), &input_len);// 获取so库路径size_t path_len = 0;napi_get_value_string_utf8(env, args[1], nullptr, 0, &path_len);char* path = new char[path_len + 1];napi_get_value_string_utf8(env, args[1], path, path_len + 1, &path_len);// 使用dlopen加载so库void* handle = dlopen(path, RTLD_LAZY);if (!handle) {napi_throw_error(env, nullptr, "Failed to load library");delete[] path;return nullptr;}// 获取函数符号ProcessStringFunc process_func = (ProcessStringFunc)dlsym(handle, "process_string");if (!process_func) {napi_throw_error(env, nullptr, "Failed to get function symbol");dlclose(handle);delete[] path;return nullptr;}// 调用函数const char* result = process_func(input);// 返回结果napi_value result_value;napi_create_string_utf8(env, result, NAPI_AUTO_LENGTH, &result_value);// 清理资源dlclose(handle);delete[] path;return result_value;
}// 模块初始化
static napi_value Init(napi_env env, napi_value exports) {napi_property_descriptor desc[] = {{"processWithDlopen", nullptr, ProcessWithDlopen, nullptr, nullptr, nullptr, napi_default, nullptr}};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;
}// 模块注册
static napi_module yourModule = {.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 RegisterYourModule() { napi_module_register(&yourModule);
}
方式二:ArkTS侧直接引用三方so库
这种方式适用于so库已经适配了Native接口的场景。
实现原理: 将so库和对应的Native侧接口文件加入到工程中,在工程中配置so库对应的模块动态依赖,在ArkTS侧通过import引入依赖接口调用so库。
注意事项: 该方案只能引用适配Native的so库,因此在编译生成so库时需要实现功能函数并向Napi注册其Native侧接口,提供对应的Native侧接口文件index.d.ts和配置文件oh-package.json5。
开发步骤:
- 准备适配Native的so库
# 项目结构
your_project/
├── entry/
│ ├── libs/
│ │ ├── arm64/
│ │ │ └── libyour_library.so
│ │ └── x86_64/
│ │ └── libyour_library.so
│ └── src/
│ └── main/
│ ├── cpp/
│ │ └── types/
│ │ └── libyour_library/
│ │ ├── index.d.ts
│ │ └── oh-package.json5
│ └── ets/
│ └── pages/
│ └── Index.ets
- 创建TypeScript类型定义
// entry/src/main/cpp/types/libyour_library/index.d.ts
export const yourFunction: (input: string) => string;
export const yourBufferFunction: (input: ArrayBuffer) => ArrayBuffer;
- 配置oh-package.json5
// entry/src/main/cpp/types/libyour_library/oh-package.json5
{"name": "libyour_library.so","types": "./index.d.ts","version": "1.0.0","description": "Your library for HarmonyOS"
}
- 在模块级oh-package.json5中声明依赖
// entry/oh-package.json5
{"name": "entry","version": "1.0.0","dependencies": {"libyour_library.so": "file:./src/main/cpp/types/libyour_library"}
}
- 在ArkTS中直接使用
// entry/src/main/ets/pages/Index.ets
import yourLibrary from 'libyour_library.so'@Entry
@Component
struct Index {@State result: string = '';build() {Row() {Column() {Text('直接调用so库').fontSize(50).fontWeight(FontWeight.Bold).onClick(() => {// 直接调用so库中的函数this.result = yourLibrary.yourFunction('Hello from ArkTS!');})Text(this.result).fontSize(30)}.width('100%')}.height('100%')}
}
AKI方式集成预编译so库
方案A:链接预编译so库
// example_aki.cpp
#include <aki/jsbind.h>
#include <aki/array_buffer.h>
#include "your_library.h" // 预编译库的头文件
#include <string>
#include <vector>// 包装预编译库的函数
std::string YourFunction(const std::string& input) {// 直接调用预编译库的函数return your_library_function(input.c_str());
}// 支持ArrayBuffer的函数
aki::ArrayBuffer YourBufferFunction(const aki::ArrayBuffer& input) {// 调用预编译库的二进制处理函数std::vector<uint8_t> result = your_library_process_buffer(input.GetData(), input.GetLength());return aki::ArrayBuffer(result.data(), result.size());
}// 注册AKI插件
JSBIND_ADDON(your_library_aki)// 注册全局函数
JSBIND_GLOBAL() {JSBIND_FUNCTION(YourFunction);JSBIND_FUNCTION(YourBufferFunction);
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(your_library_aki)# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 设置AKI根路径
set(AKI_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@ohos/aki)
set(CMAKE_MODULE_PATH ${AKI_ROOT_PATH})
find_package(Aki REQUIRED)# 源文件
set(SOURCESexample_aki.cpp # AKI包装器
)# 创建共享库
add_library(your_library_aki SHARED ${SOURCES})# 链接AKI库和预编译的so库
target_link_libraries(your_library_aki PUBLIC Aki::libjsbind${CMAKE_CURRENT_SOURCE_DIR}/../libs/${OHOS_ARCH}/libyour_library.so
)# 包含头文件路径
target_include_directories(your_library_aki PRIVATE${CMAKE_CURRENT_SOURCE_DIR}/../include
)# 设置输出目录
set_target_properties(your_library_aki PROPERTIESLIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../libRUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib
)
方案B:动态加载预编译so库
// example_aki_dynamic.cpp
#include <aki/jsbind.h>
#include <aki/array_buffer.h>
#include <dlfcn.h>
#include <string>
#include <vector>// 定义函数指针类型
typedef const char* (*YourLibraryFunc)(const char*);
typedef void* (*YourBufferFunc)(const void*, size_t, size_t*);// 全局句柄
void* library_handle = nullptr;
YourLibraryFunc your_library_function = nullptr;
YourBufferFunc your_buffer_function = nullptr;// 初始化库
bool InitializeLibrary(const std::string& library_path) {if (library_handle) {return true; // 已经初始化}library_handle = dlopen(library_path.c_str(), RTLD_LAZY);if (!library_handle) {return false;}// 获取函数符号your_library_function = (YourLibraryFunc)dlsym(library_handle, "your_library_function");your_buffer_function = (YourBufferFunc)dlsym(library_handle, "your_buffer_function");if (!your_library_function || !your_buffer_function) {dlclose(library_handle);library_handle = nullptr;return false;}return true;
}// 包装函数
std::string YourFunction(const std::string& input) {if (!your_library_function) {return "Library not initialized";}return your_library_function(input.c_str());
}aki::ArrayBuffer YourBufferFunction(const aki::ArrayBuffer& input) {if (!your_buffer_function) {return aki::ArrayBuffer(nullptr, 0);}size_t output_size = 0;void* result = your_buffer_function(input.GetData(), input.GetLength(), &output_size);if (result && output_size > 0) {aki::ArrayBuffer buffer(result, output_size);// 注意:这里需要根据实际情况管理内存return buffer;}return aki::ArrayBuffer(nullptr, 0);
}// 初始化函数
bool InitLibrary(const std::string& library_path) {return InitializeLibrary(library_path);
}// 清理函数
void CleanupLibrary() {if (library_handle) {dlclose(library_handle);library_handle = nullptr;your_library_function = nullptr;your_buffer_function = nullptr;}
}// 注册AKI插件
JSBIND_ADDON(your_library_aki_dynamic)// 注册全局函数
JSBIND_GLOBAL() {JSBIND_FUNCTION(YourFunction);JSBIND_FUNCTION(YourBufferFunction);JSBIND_FUNCTION(InitLibrary);JSBIND_FUNCTION(CleanupLibrary);
}
// types/libyour_library_aki_dynamic/index.d.ts
export function YourFunction(input: string): string;
export function YourBufferFunction(input: ArrayBuffer): ArrayBuffer;
export function InitLibrary(library_path: string): boolean;
export function CleanupLibrary(): void;
// 在ArkTS中使用
import aki from 'libyour_library_aki_dynamic.so'@Entry
@Component
struct DynamicLibraryExample {@State result: string = '';build() {Column() {Button('初始化库').onClick(() => {let projectPath = this.getUIContext().getHostContext()!.bundleCodeDir;let abiPath = deviceInfo.abiList === 'x86_64' ? 'x86_64' : 'arm64';let soLibPath = `${projectPath}/libs/${abiPath}/libyour_library.so`;const success = aki.InitLibrary(soLibPath);console.log('Library initialized:', success);})Button('调用函数').onClick(() => {this.result = aki.YourFunction('Hello from dynamic library!');})Text(this.result)Button('清理库').onClick(() => {aki.CleanupLibrary();})}}
}
📚 实际案例:HMAC-SHA256预编译库移植
案例1:Node-API方式集成预编译HMAC-SHA256库
1. 准备预编译的HMAC-SHA256库
# 预编译库文件
libs/
├── arm64/
│ └── libhmac_sha256.so
└── x86_64/└── libhmac_sha256.so# 头文件
include/
└── hmac_sha256.h
// include/hmac_sha256.h
#ifndef HMAC_SHA256_H
#define HMAC_SHA256_H#ifdef __cplusplus
extern "C" {
#endif// HMAC-SHA256计算函数
size_t hmac_sha256(const void* key, const size_t keylen,const void* data, const size_t datalen,void* out, const size_t outlen
);// 十六进制字符串转换函数
void bytes_to_hex(const void* data, size_t len, char* hex);#ifdef __cplusplus
}
#endif#endif // HMAC_SHA256_H
2. 实现Node-API包装器
// napi_init.cpp
#include <napi.h>
#include "hmac_sha256.h"
#include <string>
#include <vector>static napi_value HmacSha256Hash(napi_env env, napi_callback_info info)
{size_t argc = 2;napi_value args[2] = {nullptr};// 获取参数napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 获取key和data字符串char key[256], data[1024];size_t key_len, data_len;napi_get_value_string_utf8(env, args[0], key, sizeof(key), &key_len);napi_get_value_string_utf8(env, args[1], data, sizeof(data), &data_len);// 计算HMAC-SHA256std::vector<uint8_t> output(32);size_t result_len = hmac_sha256(key, key_len, data, data_len, output.data(), output.size());// 返回Buffervoid* buffer_data;napi_value result_buffer;napi_create_arraybuffer(env, result_len, &buffer_data, &result_buffer);memcpy(buffer_data, output.data(), result_len);return result_buffer;
}static napi_value HmacSha256Hex(napi_env env, napi_callback_info info)
{size_t argc = 2;napi_value args[2] = {nullptr};// 获取参数napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);// 获取key和data字符串char key[256], data[1024];size_t key_len, data_len;napi_get_value_string_utf8(env, args[0], key, sizeof(key), &key_len);napi_get_value_string_utf8(env, args[1], data, sizeof(data), &data_len);// 计算HMAC-SHA256std::vector<uint8_t> output(32);size_t result_len = hmac_sha256(key, key_len, data, data_len, output.data(), output.size());// 转换为十六进制字符串char hex[65];bytes_to_hex(output.data(), result_len, hex);hex[64] = '\0';// 返回字符串napi_value result_string;napi_create_string_utf8(env, hex, NAPI_AUTO_LENGTH, &result_string);return result_string;
}// 模块初始化
static napi_value Init(napi_env env, napi_value exports) {napi_property_descriptor desc[] = {{"hmacSha256Hash", nullptr, HmacSha256Hash, nullptr, nullptr, nullptr, napi_default, nullptr},{"hmacSha256Hex", nullptr, HmacSha256Hex, nullptr, nullptr, nullptr, napi_default, nullptr}};napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);return exports;
}// 模块注册
static napi_module hmacModule = {.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 RegisterHmacModule() { napi_module_register(&hmacModule);
}
3. 配置CMakeLists.txt
# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(HmacSha256Wrapper)set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})include_directories(${NATIVERENDER_ROOT_PATH}${NATIVERENDER_ROOT_PATH}/include)# 添加名为entry的库
add_library(entry SHARED napi_init.cpp)# 链接预编译的HMAC-SHA256库
target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/../../../libs/${OHOS_ARCH}/libhmac_sha256.solibace_napi.z.so
)
案例2:AKI方式集成预编译HMAC-SHA256库
// hmac_sha256_aki.cpp
#include <aki/jsbind.h>
#include <aki/array_buffer.h>
#include "hmac_sha256.h"
#include <string>
#include <vector>// 字符串输入版本
std::vector<uint8_t> HmacSha256Hash(const std::string& key, const std::string& data) {std::vector<uint8_t> output(32);size_t result_len = hmac_sha256(key.c_str(), key.length(), data.c_str(), data.length(), output.data(), output.size());output.resize(result_len);return output;
}// ArrayBuffer输入版本
aki::ArrayBuffer HmacSha256HashBuffer(const aki::ArrayBuffer& key, const aki::ArrayBuffer& data) {std::vector<uint8_t> output(32);size_t result_len = hmac_sha256(key.GetData(), key.GetLength(), data.GetData(), data.GetLength(), output.data(), output.size());aki::ArrayBuffer result(output.data(), result_len);return result;
}// 十六进制输出版本
std::string HmacSha256Hex(const std::string& key, const std::string& data) {std::vector<uint8_t> output(32);size_t result_len = hmac_sha256(key.c_str(), key.length(), data.c_str(), data.length(), output.data(), output.size());char hex[65];bytes_to_hex(output.data(), result_len, hex);hex[64] = '\0';return std::string(hex);
}// 注册AKI插件
JSBIND_ADDON(hmac_sha256_aki)// 注册全局函数
JSBIND_GLOBAL() {JSBIND_FUNCTION(HmacSha256Hash);JSBIND_FUNCTION(HmacSha256HashBuffer);JSBIND_FUNCTION(HmacSha256Hex);
}
# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(hmac_sha256_aki)# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 设置AKI根路径
set(AKI_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@ohos/aki)
set(CMAKE_MODULE_PATH ${AKI_ROOT_PATH})
find_package(Aki REQUIRED)# 源文件
set(SOURCEShmac_sha256_aki.cpp
)# 创建共享库
add_library(hmac_sha256_aki SHARED ${SOURCES})# 链接AKI库和预编译的HMAC-SHA256库
target_link_libraries(hmac_sha256_aki PUBLIC Aki::libjsbind${CMAKE_CURRENT_SOURCE_DIR}/../libs/${OHOS_ARCH}/libhmac_sha256.so
)# 包含头文件路径
target_include_directories(hmac_sha256_aki PRIVATE${CMAKE_CURRENT_SOURCE_DIR}/../include
)# 设置输出目录
set_target_properties(hmac_sha256_aki PROPERTIESLIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../libRUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib
)
✅ 最佳实践
1. 预编译库的设计原则
- C语言接口: 使用extern "C"包装,确保ABI兼容性
- 版本管理: 提供版本信息和兼容性检查
- 错误处理: 提供完善的错误码和错误信息
- 内存管理: 明确内存所有权和生命周期
2. 集成方式选择
- 静态链接: 适用于小型库,编译时链接
- 动态链接: 适用于大型库,运行时加载
- 插件化: 适用于可扩展的库,支持热插拔
3. 性能优化
- 延迟加载: 只在需要时加载库
- 缓存机制: 缓存函数指针,避免重复查找
- 内存池: 使用内存池减少内存分配开销
4. 错误处理
// 错误处理示例
static napi_value SafeCallLibrary(napi_env env, napi_callback_info info) {try {// 调用库函数return YourFunction(env, info);} catch (const std::exception& e) {napi_throw_error(env, nullptr, e.what());return nullptr;} catch (...) {napi_throw_error(env, nullptr, "Unknown error occurred");return nullptr;}
}
❓ 常见问题
Q1: 如何处理不同架构的so库?
A: 在CMakeLists.txt中使用条件判断:
if(OHOS_ARCH STREQUAL "arm64")set(LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../libs/arm64")
elseif(OHOS_ARCH STREQUAL "x86_64")set(LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../libs/x86_64")
endif()target_link_libraries(your_library PUBLIC ${LIB_PATH}/libyour_library.so)
Q2: 如何处理so库的依赖问题?
A: 确保所有依赖的so库都在正确的路径下,并在CMakeLists.txt中正确链接:
target_link_libraries(your_library PUBLIC ${LIB_PATH}/libyour_library.so${LIB_PATH}/libdependency1.so${LIB_PATH}/libdependency2.so
)
Q3: 如何处理so库的版本兼容性?
A: 在库中提供版本检查函数:
// 版本检查
int get_library_version() {return 100; // 版本号
}// 兼容性检查
bool check_compatibility(int required_version) {return get_library_version() >= required_version;
}
Q4: 如何处理so库的加载失败?
A: 提供完善的错误处理和回退机制:
bool LoadLibrarySafely(const std::string& path) {void* handle = dlopen(path.c_str(), RTLD_LAZY);if (!handle) {console.error("Failed to load library:", dlerror());return false;}// 验证库的完整性if (!ValidateLibrary(handle)) {dlclose(handle);return false;}return true;
}
Q5: 如何在纯ArkTS工程中使用预编译so库?
A: 使用模块动态依赖的方式,确保so库已经适配了Native接口:
// oh-package.json5
{"dependencies": {"libyour_library.so": "file:./src/main/cpp/types/libyour_library"}
}
🎉 总结
预编译so库的集成方式为HarmonyOS应用开发提供了更大的灵活性:
- 模块化开发: 支持团队协作和代码复用
- 性能优化: 避免重复编译,提高构建效率
- 版本管理: 支持独立的版本控制和兼容性管理
- 多种集成方式: 支持静态链接、动态链接和插件化集成
选择合适的集成方式取决于具体的项目需求和约束条件。无论选择哪种方式,都要确保:
- 接口设计合理: 提供ArkTS友好的接口
- 错误处理完善: 提供详细的错误信息和回退机制
- 性能优化: 减少不必要的开销
- 兼容性保证: 确保不同版本和架构的兼容性
参考链接
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/blob/39de4c9403b60f14c4352d20c32934a983936956/lycium/doc/ohos_use_sdk/OHOS_SDK-Usage.md
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/blob/39de4c9403b60f14c4352d20c32934a983936956/lycium/doc/app_calls_third_lib.md
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/tree/39de4c9403b60f14c4352d20c32934a983936956/lycium
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/blob/39de4c9403b60f14c4352d20c32934a983936956/lycium/Buildtools/README.md
https://gitcode.com/openharmony-sig/tpc_c_cplusplus
https://gitcode.com/openharmony-sig/tpc_c_cplusplus/blob/master/docs/adapter_windows.md
https://gitee.com/javen678/hello-ohos-napi/blob/master/doc/README.md