接上篇文章,上篇介绍了在有源码的情况下的编译。更多时候三方库的移植,可能只提供给你了arm64平台下的so库和头文件。那么如何移植到harmonyOS平台下呢?总结一句话就是利用so库和头文件,再封装成arkts侧能用的arkts接口,使用napi或者aki的方式。
📋 目录
预编译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.so
libace_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(SOURCES
example_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 PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib
RUNTIME_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-SHA256
std::vector<uint8_t> output(32);
size_t result_len = hmac_sha256(key, key_len, data, data_len, output.data(), output.size());
// 返回Buffer
void* 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-SHA256
std::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.so
libace_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(SOURCES
hmac_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 PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib
RUNTIME_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