HarmonyOS 鸿蒙Next 如何执行本地so文件中的函数?

发布于 1周前 作者 h691938207 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 如何执行本地so文件中的函数?

原生写法: public class CMT2300 {  static { System.loadLibrary(“cmt”); }  public static native byte[] cmtConfig(int fre, int dev);  }

2 回复

非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/ohos_use_sdk/OHOS_SDK-Usage.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;

@Entry

@Component

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文件中的函数,通常需要遵循以下步骤:

  1. 加载.so文件:使用System.loadLibrarySystem.load方法加载本地库。loadLibrary会搜索系统库路径,而load需要指定完整路径。

  2. 声明本地方法:在Java类中,使用native关键字声明需要调用的本地方法。这些方法需要与.so文件中实现的函数名称和签名匹配。

  3. 生成头文件:通过javac编译Java类,然后使用javah工具生成包含本地方法声明的C/C++头文件。

  4. 实现本地方法:在C/C++中实现头文件中的方法,并编译成.so文件。确保函数名称和签名与Java中声明的匹配,同时考虑JNI命名规则(如Java_ClassName_methodName)。

  5. 调用本地方法:在Java代码中直接调用通过native声明的本地方法,系统会自动调用.so文件中的实现。

注意,在鸿蒙系统中,可能需要特别注意库文件的路径和权限设置,以及JNI接口的具体实现方式。确保所有路径和名称正确无误,以避免加载失败或运行时错误。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部