HarmonyOS 鸿蒙Next 如何执行本地so文件中的函数?
HarmonyOS 鸿蒙Next 如何执行本地so文件中的函数?
非native模块无法直接引用so库。
引用外部.so库分为两类
一、so为鸿蒙编译器编译,可在libs下创建一个架构文件夹,文件架构为:
libs/架构名(arm64-v8a,armeabi-v7a,x86_64)/xxx.so
然后在ets侧进行引用,引用方式为:
import {方法名} from ‘xxxx.so’
二、so为其他编译器编译,需要走适配流程,流程如下:
第一步,配置交叉编译环境,https://gitee.com/openharmony-sig/tpc_c_cplusplus/blob/master/lycium/Buildtools/README.md
第三步,集成进项目,https://gitee.com/openharmony-sig/tpc_c_cplusplus/blob/master/lycium/doc/app_calls_third_lib.md
补充一下,“一、so为鸿蒙编译器编译,可在libs下创建一个架构文件夹......"中所说的libs指的是”entrty/libs'
ArkTS侧引用鸿蒙编译的so库详细可参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-ndk-21-V5
导出参考:
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;
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
Native侧集成三方SO库:https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs-V5/faqs-ndk-5-V5
鸿蒙引用三方so库(鸿蒙编译器生成的so库或者经过鸿蒙化的so库),参考:https://gitee.com/openharmony-sig/tpc_c_cplusplus/blob/master/lycium/doc/app_calls_third_lib.md 可以实现。
一、创建so库——如何引用cpp源文件
1)创建Native项目
命名为“MyFunction”——>点击“finish”
2)在“entry\src\main\cpp"下创建“MyMath”文件夹,并新建“myMath.h”和“myMath.cpp”文件。
3)“myMath.h”和“myMath.cpp”中C++方法实现
//myMath.h
#ifndef MYFUNCTION_MYMATH_H
#define MYFUNCTION_MYMATH_H
double myPlus(double a,double b); //加
double myMinus(double a,double b); //减
double myMultip(double a,double b); //乘
double myDivis(double a,double b); //除
#endif //MYFUNCTION_MYMATH_H
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
//myMath.cpp
#include “MyMath/myMath.h”
double myPlus(double a,double b)
{
return a + b + 200;
}
double myMinus(double a,double b)
{
return a - b +200;
}
double myMultip(double a,double b)
{
return ab10;
}
double myDivis(double a,double b)
{
return ab30/5;
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
4)CMakeLists.txt 中添加包含目录 + 向库的源文件列表中添加“myMath.cpp”
CMakeLists.txt完整示例:
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(MyFunction)
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
./MyMath)
add_library(entry SHARED napi_init.cpp
MyMath/myMath.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so)
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
5) “napi_init.cpp"引用源文件
a. 引用头文件"myMath.h”
b. 添加"Plus" 和"Minus"函数,引用myMath中的"myPlus"和"myMinus"函数
c. 将"Plus" 和"Minus"函数添加到 ArkTS接口与C++接口的绑定和映射
//napi_init.cpp
#include “napi/native_api.h”
#include “MyMath/myMath.h”
static napi_value Add(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);
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;
}
static napi_value Plus(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);
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);
double cResult= myPlus(value0, value1);
napi_value sum ;
napi_create_double(env, cResult, &sum);
return sum;
}
static napi_value Minus(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);
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);
double cResult= myMinus(value0, value1);
napi_value sum ;
napi_create_double(env, cResult, &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 },
{ “plus”, nullptr, Plus, nullptr, nullptr, nullptr, napi_default, nullptr },
{ “minus”, nullptr, Minus, 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);
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
6)在index.d.ts文件中,将接口函数导出。
//index.d.ts
export const add: (a: number, b: number) => number;
export const plus: (a: number, b: number) => number;
export const minus: (a: number, b: number) => number;
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
7)ArkTS侧调用 “entry\src\main\ets\pages\index.ets”
//index.d.ts
export const add: (a: number, b: number) => number;
export const plus: (a: number, b: number) => number;
export const minus: (a: number, b: number) => number;
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
//index.ets
import { hilog } from ‘@kit.PerformanceAnalysisKit’;
import testNapi from ‘libentry.so’;
struct Index {
@State message: string = ‘原本加法:’;
@State message1: string = ‘新-加法:’;
@State message2: string = ‘新-减法:’;
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message += testNapi.add(2, 3);
hilog.info(0x0000, ‘testTag’, ‘原生加法 2 + 3 = %{public}d’, testNapi.add(2, 3));
})
Text(this.message1)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message1 += testNapi.plus(2, 3);
hilog.info(0x0000, ‘testTag’, ‘新加法 2 + 3 = %{public}d’, testNapi.plus(2, 3));
})
Text(this.message2)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.message2 += testNapi.minus(2, 3);
hilog.info(0x0000, ‘testTag’, ‘新减法法 2 + 3 = %{public}d’, testNapi.minus(2, 3));
})
}
.width(‘100%’)
}
.height(‘100%’)
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
二、创建so库——如何给so库改名字
为了区分我们生成的so库跟Native工程默认生成的“libentry.so”库,接下来给so库改个名字。
1)在CMakeList里修改对应的模块名;
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(MyFunction)
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
./MyMath)
add_library(myMath SHARED napi_init.cpp
MyMath/myMath.cpp)
target_link_libraries(myMath PUBLIC libace_napi.z.so)
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
2)在对应.cpp文件的注册模块中的.nm_modname修改so的名字;
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = “myMath”,
.nm_priv = ((void*)0),
.reserved = { 0 },
};
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
3)在src/main/cpp/types/lib模块名/oh-package.json5中修改so的名字;
{
“name”: “libmyMath.so”,
“types”: “./Index.d.ts”,
“version”: “1.0.0”,
“description”: “Please describe the basic information.”
}
4)在模块级oh-package.json5中修改依赖的so名字,同时将其文件夹改名;
{
“name”: “myMath”,
“version”: “1.0.0”,
“description”: “Please describe the basic information.”,
“main”: “”,
“author”: “”,
“license”: “”,
“dependencies”: {
“libmyMath.so”: “file:./src/main/cpp/types/libmyMath”
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
5)修改模块级的module.json5中的模块名;
{
“module”: {
“name”: “myMath”,
“type”: “entry”,
“description”: “$string:module_desc”,
“mainElement”: “EntryAbility”,
“deviceTypes”: [
“phone”
],
“deliveryWithInstall”: true,
“installationFree”: false,
“pages”: “$profile:main_pages”,
“abilities”: [
…(省略)
],
“extensionAbilities”: [
…(省略)
]
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
6)修改工程级的buid-profile.json5中的模块名。
{
“app”: {
“signingConfigs”: [
],
“products”: [
…(省略)
],
“buildModeSet”: [
…(省略)
]
},
“modules”: [
{
“name”: “myMath”,
“srcPath”: “./entry”,
“targets”: [
{
“name”: “default”,
“applyToProducts”: [
“default”
]
}
]
}
]
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
7)改名(ArkTS侧“import”处引用的so名称)
import testNapi from ‘libmyMath.so’;
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
三、Native侧引用so库及so库中的函数
1)创建Native项目
命名为“NeedFunction”——>点击“finish”
2)将三方库生成的二进制文件拷贝到应用工程目录
为了更好的管理应用集成的三方库,
a. 在应用工程的cpp目录新建一个thirdparty目录
b. thirdparty下创建"libmyMath"文件夹(用于存放“libmyMath”库相关的文件)
c. “libmyMath"下再创建架构文件夹,例如"arm64-v8a"和"x86_64”
d. "arm64-v8a"和"x86_64"文件夹下再分别创建”include“文件夹和”lib"文件夹
e. 将"myMath.h"头文件拷贝到该对应的架构目录下的"include"文件夹中
最终的文件结构:(以"arm64-v8a"为例)
entry/src/main/cpp/types/thirdparty/libmyMath/arm64-v8a/include/myMath.h
f. 将生成的so文件拷贝到该对应的架构目录下的"lib"文件夹中。
最终的文件结构:(以"arm64-v8a"为例)
entry/src/main/cpp/types/thirdparty/libmyMath/arm64-v8a/lib/libmyMath.so
3)该三方库二进制文件为so文件,还需要将so文件拷贝到工程目录的entry/libs/${OHOS_ARCH}/目录下
4)CMakeLists.txt配置对应链接
配置链接只需要在cpp目录的CMakeLists.txt文件中添加对应target_link_libraries。
target_link_libraries(entry PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libmyMath/${OHOS_ARCH}/lib/libmyMath.so)
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
5)配置头文件路径
配置链接只需要在cpp目录的CMakeLists.txt文件中添加对应target_include_directories
target_include_directories(entry PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libmyMath/${OHOS_ARCH}/include)
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
6)Native侧引用so库
#include “napi/native_api.h”
#include “thirdparty/libmyMath/x86_64/include/myMath.h”
static napi_value Add(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);
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);
double cResult = 0;
cResult = myMultip(value0, value1);
napi_value sum;
napi_create_double(env, cResult, &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);
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
注:上述代码中加载了myMultip方法,native侧能加载的方法是myMath.h中的函数:
double myPlus(double a,double b); //加
double myMinus(double a,double b); //减
double myMultip(double a,double b); //乘
double myDivis(double a,double b); //除
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
而不是index.d.ts文件中导出的接口函数,这是给ArkTS侧调用的
//index.d.ts
export const add: (a: number, b: number) => number;
export const plus: (a: number, b: number) => number;
export const minus: (a: number, b: number) => number;
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
以我前面的举例来看,myMath.h头文件是来自于so库项目的同一个文件,之所以拆开只是为了让整个项目文件夹看起来比较有条理。
在HarmonyOS(鸿蒙)系统中执行本地.so
文件中的函数,通常需要遵循以下步骤:
-
加载.so文件:使用
System.loadLibrary
或System.load
方法加载本地库。loadLibrary
会搜索系统库路径,而load
需要指定完整路径。 -
声明本地方法:在Java类中,使用
native
关键字声明需要调用的本地方法。这些方法需要与.so
文件中实现的函数名称和签名匹配。 -
生成头文件:通过
javac
编译Java类,然后使用javah
工具生成包含本地方法声明的C/C++头文件。 -
实现本地方法:在C/C++中实现头文件中的方法,并编译成
.so
文件。确保函数名称和签名与Java中声明的匹配,同时考虑JNI命名规则(如Java_ClassName_methodName
)。 -
调用本地方法:在Java代码中直接调用通过
native
声明的本地方法,系统会自动调用.so
文件中的实现。
注意,在鸿蒙系统中,可能需要特别注意库文件的路径和权限设置,以及JNI接口的具体实现方式。确保所有路径和名称正确无误,以避免加载失败或运行时错误。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html