一、介绍
本文用于说明和介绍编译底层C++代码生成动态库so,并且在harmonyOS项目中如何进行使用和集成,通过openharmony SDK在linux环境下编译动态库so,可打包其他静态库.a打包到此so,然后通过natvie项目(harmonyOS),集成此so并在项目中使用。
二、编译准备
1.1. 准备openmarmony SDK
1.2. 准备DevecoStudio
准备好开发工具,并配置好环境
https://developer.huawei.com/consumer/cn/deveco-studio/
1.3. 准备待编译的C++项目和代码
主要用于在linux环境下编译动态库so,并且打包一些.a第三方库静态库整合到一个so中,后续在鸿蒙项目中进行使用
三、编译步骤和说明
3.1. 使用openharmony SDK编译C++代码
可参考下面文档进行开发,这里就不细说了,后续会出单独说明文章:
https://gitee.com/openharmony-sig/tpc_c_cplusplus/blob/master/lycium/doc/ohos_use_sdk/OHOS_SDK-Usage.md#ohos-sdk-%E4%BD%BF%E7%94%A8
3.2. 创建native c++项目(DevecoStudio)
3.2.1.创建项目
3.2.2 native c++项目结构
so:这里使用的是3.1中编译好的so库
-
index.d.ts: napi中定义的接口(相当于jni中定义好的接口的头文件)
oh-package.json5: 编译输入的接口说明文件和输出的lib.so文件
*注:(此处的so代表通过我们之前3.1编译好的so库在native项目中二次打包编译生成的so库文件,最终需要在ets中进行应用的so库,也是最终产物)
{
"name": "libentry.so", // so输出产出
"types": "./index.d.ts", // 接口定义
"version": "",
"description": "Please describe the basic information."
}
- CMakeLists.txt:编译过android ndk的都知道这里不详细说明了,下面是截图
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(MyApplication)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED hello.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so)
- hello.cpp:关键文件,相当于android编译中的jni的cpp接口封装文件,harmonyOS中定义为napi,通过此接口的封装可调用底层c++代码的接口.
这里定义了一个add方法并包含输入和输出的参数。
#include "napi/native_api.h"
static napi_value Add(napi_env env, napi_callback_info info)
{
size_t requireArgc = 2;
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);
napi_value sum;
napi_create_double(env, value0 + value1, &sum);
return sum;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{ "add", nullptr, Add, 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);
}
至此harmonyOS-native c++项目架构说明结束。
3.3.集成和使用已有的动态库so
3.3.1 准备好3.1中编译好的so库,比如HosTest.so
注意:实际编译可能需要生成多个abi架构的so库,armeabi-v7a和arm64-v8a
3.3.2 将HosTest.so放入libs文件夹(3.2中创建的项目)
这里由于测试只添加了v7a,后续可自行添加.
修改abi配置文件,build-profile.json5文件
3.3.3 将HosTest.so放入libs文件夹(3.2中创建的项目)
① 创建thirdPart文件在cpp下(名称可自定义)
② 创建或引入Hello.h文件,此文件为c++项目底层定义的暴露给外部的接口的头文件, 可以看到我们在Hello.h中定义了一个print的方法,并且返回值为string格式,此print方法为我们HosTest.so中的c++底层方法.
#ifndef __HELLO_H__
#define __HELLO_H__
#include <string>
class Hello
{
public:
std::string print();
};
#endif
③ napi接口定义文件中引用Hello
// 此处引用我们上面定义的头文件,就可以使用HosTest.so中的c++接口了
#include "thirdPart/Hello.h"
#include "napi/native_api.h"
#include <string.h>
static napi_value FN_hello_print(napi_env env, napi_callback_info info) {
Hello ch;
std::string string1= ch.print();
char *str = "加油打工人";
napi_value result;
napi_create_string_utf8(env,string1.c_str(),string1.length(),& result);
return result;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {{"print", NULL, FN_hello_print, NULL, NULL, NULL, napi_default, NULL}};
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 = NULL,
.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文件
修改cpp下的oh-package.json5: 编译输入的接口说明文件和输出的lib.so文件
*注:(此处的so代表通过我们之前3.1编译好的so库在native项目中二次打包编译生成的so库文件,最终需要在ets中进行应用的so库,也是最终产物)
{
"name": "HosSIDCard .so", // so输出产出,这里需要修改成上面定义的名称
"types": "./index.d.ts", // 接口定义
"version": "",
"description": "Please describe the basic information."
}
完整代码:
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(HosNatvieProject)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(HosOutSO SHARED hello.cpp) #此处添加 HosOutSO 代表产出的so名称
target_link_libraries(HosSIDCard PUBLIC
libace_napi.z.so
libhilog_ndk.z.so
${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH}/HosTest.so #此处是引用的第三方库
)
target_include_directories(HosSIDCard PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdPart/)
⑤ 调用so文件接口
import hilog from '@ohos.hilog';
import HosTest from 'libHosTest.so'; // 引用最终产物so库
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
console.error(HosTest.print()); // 使用接口print
})
}
.width('100%')
}
.height('100%')
}
}
至此,整体的第三方so库的使用已经完成。如有问题可以参考下面的问题汇总。
四、问题汇总
4.1.找不到引用的SO对象以及使用的底层方法
HosTest.print()报错
解决方案:
① 检查cpp下的.ts文件是否定义了接口
② 检查CMakeLists文件和cpp下的oh-package-json5文件
HosOutSo是否配置和应用的so一致
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(HosNatvieProject)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(HosOutSO SHARED hello.cpp) #此处添加 HosOutSO 代表产出的so名称
target_link_libraries(HosSIDCard PUBLIC
libace_napi.z.so
libhilog_ndk.z.so
${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH}/HosTest.so #此处是引用的第三方库
)
target_include_directories(HosSIDCard PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdPart/)
oh-package-json5文件的so配置是否一致
{
"name": "HosSIDCard.so", // so输出产出,这里需要修改成上面定义的名称
"types": "./index.d.ts", // 接口定义
"version": "",
"description": "Please describe the basic information."
}
③ Hello.h头文件是否包含
④ napi.cpp的文件是否正常
此问题原因基本就是某个环节配置的名称不匹配导致的,可以逐步检查解决。