一个关于HarmonyOS应用研发的专栏,实践中见证你的实力提升
背景
上篇文章"HarmonyOS NAPI入门" 中,介绍了如何通过C/C++源码方式集成功能.
本篇将会介绍另外两种集成方式:
- NAPI源码 + 动态库(即 .so)
- TS接口 + 动态库(即 .so)
Native C++应用研发概况
Native C++应用场景,总结起来就如下三种
-
HarmonyOS NAPI入门这篇文章采用的是"全部源码"方式
-
"部分源码+动态库": 国密算法SM4已经提前被编译成了so文件,这个时候我们只需在CMakeList.txt文件中稍许配置一下,即可完成so的引用
-
"TS接口+动态库": 国密算法SM4和NAPI接口文件均已被提前编译成了so文件,这个时候只需添加TS声明文件和配置动态库依赖
部分源码&动态库
生成SM4动态库
借助"全部源码"接入方式,先修改CMakeList.txt, 让其在编译时生成"sm4.so"文件
scss
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(HarmonyLearn)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(customnapi SHARED main.cpp
util.cpp)
#源码生成 sm4.so
#add_library(sm4 SHARED sm4.cpp)
target_link_libraries(customnapi PUBLIC libace_napi.z.so sm4)
编译"entry"模块(Build -> Build Hap(s)/APP(s) -> Build Hap(s))
编译完成后的产物,可以在如下路径下找到
entry/build/default/intermediates/libs/default/
创建C++文件夹,导入文件
在entry主模块中的 "src/main" 路径下创建 "cpp" 文件夹,然后添加sm4动态库需要的透文件sm4.h,之后在"cpp"文件夹下创建一个"libs"文件夹,导入libsm4.so动态库
最终的路径
- 头文件: scr/main/cpp/include/sm4.h
- 动态库: scr/main/cpp/libs/arm64-v8a/libsm4.so, scr/main/cpp/libs/armeabi-v7a/libsm4.so
修改CMakeFile.txt
因为我们用到了提前编译好的libsm4.so, 所以在引入时和源码生成方式稍微不一样
差异
源码生成库
add_library(sm4 SHARED sm4.cpp)
预制库生成库
add_library(sm4 SHARED IMPORTED)
set_target_properties(sm4 PROPERTIES IMPORTED_LOCATION 路径/libsm4.so)
CMakeFile源文件
scss
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(HarmonyLearn)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(customnapi SHARED main.cpp
util.cpp)
#设置 libsm4.so路径
set(my_lib_path ${CMAKE_CURRENT_SOURCE_DIR}/libs/)
message("my_lib_path = ${my_lib_path}${OHOS_ARCH}/")
#生成 sm4.so
add_library(sm4 SHARED IMPORTED)
set_target_properties(sm4 PROPERTIES IMPORTED_LOCATION ${my_lib_path}${OHOS_ARCH}/libsm4.so)
#链接customnapi动态库
target_link_libraries(customnapi PUBLIC libace_napi.z.so sm4)
配置应用Native编译选项
在"entry"模块中的build-profile.json5文件中,设置CMakeFist.txt路径和动态库CPU架构
编写NAPI接口
样例中main.cpp文件, 文件详情见代码库
ini
// entry\src\main\cpp\main.cpp
// 引入N-API相关头文件。
#include "napi/native_api.h"
#include <csignal>
#include <iostream>
#include "sm4.h"
#include <sstream>
#include "util.h"
/**
* 测试国密SM4接口: 加密
*
* @param env
* @param info
* @return
*/
static napi_value encryptBySm4(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);
size_t typeLen = 0;
napi_get_value_string_utf8(env, args[0], nullptr, 0, &typeLen);
char *extContent = new char[typeLen + 1];
napi_get_value_string_utf8(env, args[0], extContent, typeLen + 1, &typeLen);
sm4 s;
s.setType(sm4::CBC);
s.setKey("1234567890123456");
s.setIv("asdfghjklzxcvbnm");
std::string originStr = extContent;
std::string encryptData = s.encrypt(originStr);
util k;
std::string hexStr = k.strToHex(encryptData);
const int length = hexStr.length();
char *char_array = new char[length + 1];
strcpy(char_array, hexStr.c_str());
std::cout << "你好" << char_array;
napi_value result;
napi_create_string_utf8(env, char_array, length + 1, &result);
delete[] char_array;
return result;
}
EXTERN_C_START
// Init将在exports上挂上Add/NativeCallArkTS这些native方法,此处的exports就是开发者import之后获取到的ArkTS对象。
static napi_value Init(napi_env env, napi_value exports) {
// 函数描述结构体,以Add为例,第三个参数"Add"为上述的native方法,
// 第一个参数"add"为ArkTS侧对应方法的名称。
napi_property_descriptor desc[] = {
......
{"encryptBySm4", nullptr, encryptBySm4, nullptr, nullptr, nullptr, napi_default, nullptr},
......
};
// 在exports这个ArkTS对象上,挂载native方法。
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
// 准备模块加载相关信息,将上述Init函数与本模块名等信息记录下来。
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "customnapi",
.nm_priv = ((void *)0),
.reserved = {0},
};
// 打开so时,该函数将自动被调用,使用上述demoModule模块信息,进行模块注册相关动作。
extern "C" __attribute__((constructor)) void RegisterModule(void) {
napi_module_register(&demoModule);
}
ets使用动态库
在ets文件中,通过 import customnapi from "libcustomnapi.so"
方式,即可完成引用。
开始采用国密SM4加密字符串,customnapi.encryptBySm4("测试")
至此,"NAPI源码 + 动态库" 方式已介绍完成
TS接口&动态库
这里假设我们已经准备好了动态库,假如有两个动态库:libcustomnapi.so 和 libc++_shared.so
生成动态库
关于生成动态库的过程,可参见 "生成SM4动态库" 部分
创建C++文件夹,导入TS接口文件
在entry主模块中的 "src/main" 路径下创建 "cpp/types/libcustomnapi" 文件夹.
- 创建动态库对外暴露的接口声明文件, index.d.ts
- 创建动态库与接口文件映射的配置文件,oh-package.json5
index.d.ts文件
typescript
export const add: (a: number, b: number) => number;
export const nativeCallArkTS: (a: object) => number;
export const passStr: (a: string) => string;
export const encryptBySm4: (a: string) => string;
export const decryptBySm4: (a: string) => string;
export const generatorMockArray: (a: number) => Array<string>;
export const getCustomObject: () => Object;
oh-package.json5
json
{
"name": "libcustomnapi.so",
"types": "./index.d.ts",
"version": "",
"description": "Please describe the basic information."
}
导入动态库
在entry主模块中创建一个"libs"文件,其路径为: entry/libs, 与 entry/src 平级
配置应用对动态库的依赖
在entry主模块中的 oh-package.json5 文件中,增加如下内容
erlang
......
"devDependencies": {
"libcustomnapi.so": "file:./src/main/cpp/types/libcusomnapi"
},
"dependencies": {
"libcustomnapi.so": "file:./src/main/cpp/types/libcusomnapi"
},
......
ets使用动态库
在ets文件中,通过 import customnapi from "libcustomnapi.so"
方式,即可完成引用。
开始采用国密SM4加密字符串,customnapi.encryptBySm4("测试")
至此,"TS接口&动态库" 方式已介绍完成
结尾
关于Native C++应用的所有场景已介绍结束,如果你对这方面还想进一步实践一下,可以再研究一下 "codelabs/XComponent"中的代码,找找感觉。 样例链接:gitee.com/harmonyos/c...
如果想更进一步学习NAPI接口的使用,可参见openHarmony中的样例,其用法与HarmonyOS中的NAPI接口使用是一致的。
关于CMakeList.txt文件的一些使用方法,可以参考 Android 的官方介绍developer.android.google.cn/studio/proj...
祝好运!