HarmonyOS Next鸿蒙NDK使用示例

创建一个Native C++项目

        跟普通项目相比,主要区别是多了一个cpp文件夹、oh-package.json5中的dependencies引入还有build-profile.json5中的externalNativeOptions配置,abiFilters是支持的CPU架构,目前移动端项目只支持arm64-v8a、x86_64两种。

        普通项目也可以复制以下文件/配置到对应位置变成一个Native C++项目。但要注意把一些entry的字符替换成你的module名称。如果同样是entry就不用修改。

生成一个测试用的so库

编写测试代码

        新建两个文件test_c.cpp和test_c.h,位置如下

test_c.h

#ifndef NATIVETEST_TEST_C_H
#define NATIVETEST_TEST_C_H

class test_c {
public:
    explicit test_c();
    ~test_c();

    int add(int a, int b);
    int sub(int a, int b);
};

#endif //NATIVETEST_TEST_C_H

test_c.cpp

#include "test_c.h"
test_c::test_c() {}
test_c::~test_c() {}
int test_c::add(int a, int b) {
    return a + b;
}

int test_c::sub(int a, int b) {
    return a - b;
}

修改CMakeLists.txt文件

        前面都是建项目时默认写好的语句,主要加了一行add_library(test_c SHARED test_c.cpp)

cmake_minimum_required(VERSION 3.5.0)
#…………中间省略
#第一个test_c是指将要生成的so文件名,最终的名称会变成libtest_c.so
add_library(test_c SHARED test_c.cpp)

构建so库

        点击Build -> Build Hap(s)/APP(s) -> Build Hap(s), 生成的so文件在以下目录。生成后就可以把test_c.cpp删掉了。

引用so库

复制so文件到对应目录

        在cpp目录下建一个libs文件夹,按照CPU架构把so文件放到对应的文件夹中(同时把所有.h头文件放到include文件夹中,这里因为在上个步骤中已经写好了test_c.h,所以只需放入so文件)

修改CMakeLists.txt文件

主要是加了最后一行target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/libs/${OHOS_ARCH}/xxxxx.so),具体so文件的名称自行修改

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
#项目名称,创建项目时填的值
project(NativeTest)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

if(DEFINED PACKAGE_FIND_FILE)
    include(${PACKAGE_FIND_FILE})
endif()

# 添加头文件.h目录,包括cpp,cpp/include,告诉cmake去这里找到代码引入的头文件
# 一般头文件都放在cpp/include下
include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so)

#add_library(test_c SHARED test_c.cpp)
#会根据不同的架构去不同的目录下找到对应的so文件
target_link_libraries(entry PUBLIC ${NATIVERENDER_ROOT_PATH}/libs/${OHOS_ARCH}/libtest_c.so)

调用so库中的方法

        以下代码可以复制进创建项目时生成的napi_init.cpp文件中

#include "test_c.h"
// 如果是用C编写的库,需要在extern "C"{}中包裹,否则会出现链接错误
// extern "C"{
//     #include "test_c.h"
// }
#include <hilog/log.h>//日志输出所需要的库
#pragma comment(lib, "libhilog_ndk.z.so")//日志输出所需要的库
void test_demo()
{
    // 这里只是演示调用引用的so库的方法
    test_c test;
    int r1 = test.add(10, 5);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.add(10, 5) = %{public}d", r1);
    int r2 = test.sub(10, 5);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.sub(10, 5) = %{public}d", r2);
}

napi_init.cpp完整代码

#include "napi/native_api.h"
#include "test_c.h"
// 如果是用C编写的库,需要在extern "C"{}中包裹,否则会出现链接错误
// extern "C"{
//     #include "test_c.h"
// }
#include <hilog/log.h>//日志输出所需要的库
#pragma comment(lib, "libhilog_ndk.z.so")//日志输出所需要的库
void test_demo()
{
    // 这里只是演示调用引用的so库的方法
    test_c test;
    int r1 = test.add(10, 5);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.add(10, 5) = %{public}d", r1);
    int r2 = test.sub(10, 5);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.sub(10, 5) = %{public}d", r2);
}
static napi_value Add(napi_env env, napi_callback_info info)
{
    test_demo();
    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);
}

使用Node-API实现ArkTS与C/C++模块之间的交互

定义接口

        在index.d.ts文件中,提供ArkTS/JS侧的接口方法。

//一个同步返回的加法计算
export const add: (a: number, b: number) => number;
//一个异步返回的加法计算
export const addWithCallBack: (a: number, b: number, callBack: (result:number) => void) => number;
//各种数据类型的参数传参demo,这里只列了几个常见的
export const paramsTest: (a: number, b: string, c:boolean, d:string[], e:ArrayBuffer) => void;

接口实现

        在index.d.ts定义的接口需在napi_init.cpp有对应实现。

#include "napi/native_api.h"
#include "test_c.h"
#include <thread>
#include <hilog/log.h>//日志输出所需要的库
#pragma comment(lib, "libhilog_ndk.z.so")//日志输出所需要的库
void test_demo()
{
    // 这里只是演示调用引用的so库的方法
    test_c test;
    int r1 = test.add(10, 5);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.add(10, 5) = %{public}d", r1);
    int r2 = test.sub(10, 5);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", " test.sub(10, 5) = %{public}d", r2);
}

//一个同步返回的加法计算
static napi_value Add(napi_env env, napi_callback_info info)
{
    test_demo();//测试调用so库的方法
    
    size_t argc = 2; // 参数个数
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
  
    // 获取第一个参数
    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;
}

//定义需要传递给异步工作的数据结构
struct CallbackContext {
    napi_env env = nullptr;
    napi_ref recvCallbackRef = nullptr;
    napi_async_work work;
    //需要传入的参数
    double a;
    double b;
    //返回的参数
    double result;
};

// 这里可以进行耗时操作, 方法名可修改,但参数固定
void AddAsync(napi_env env, void *data) {
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddAsync is called");
    // 获取传入的参数
    CallbackContext *context = (CallbackContext *)data;
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "context.a: %{public}f", context->a);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "context.b: %{public}f", context->b);
    // 模拟耗时操作
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 睡眠1秒
    // 计算结果
    context->result = context->a + context->b;
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddAsync end");
}

//AddAsync执行完毕后会自动调用这个方法, 方法名可修改,但参数固定
void AddCallBack(napi_env env, napi_status status, void *data) {
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddCallBack is called");
    CallbackContext *context = (CallbackContext *)data;
    napi_value recvCallback = nullptr;
    napi_get_reference_value(context->env, context->recvCallbackRef, &recvCallback);
    // 因为回调方法只有一个参数,  若有多个参数要给每个参数都赋值
    int size = 1;
    napi_value argv[size];
    napi_create_double(env, context->result, &argv[0]);

    napi_value ret;
    napi_call_function(env, nullptr, recvCallback, size, argv, &ret);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "AddCallBack delete");
    napi_delete_reference(context->env, context->recvCallbackRef);
    napi_delete_async_work(context->env, context->work);
    delete context;
}

//一个异步返回的加法计算
static napi_value AddWithCallBack(napi_env env, napi_callback_info info)
{
    size_t argc = 3;
    napi_value args[3] = {nullptr};
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
   
    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    // 获取回调函数
    napi_ref recvCallbackRef;
    napi_create_reference(env, args[2], 1, &recvCallbackRef); 
    
    CallbackContext *context = new CallbackContext;
    context->env = env;
    context->recvCallbackRef = recvCallbackRef;
    context->a = value0;
    context->b = value1;
    //异步调用
    napi_value resource;
    //第二个参数为当前方法名
    napi_create_string_latin1(context->env, "AddWithCallBack", NAPI_AUTO_LENGTH, &resource);
    //创建异步工作,AddAsync为耗时操作的方法,AddCallBack为耗时操作完成后的回调方法,可替换成自己实际所需的方法
    napi_create_async_work(context->env, nullptr, resource, AddAsync, AddCallBack, context,
                           &context->work);
    napi_queue_async_work(context->env, context->work); // 实现在UI主线程调用

    // 直接返回空值,实际返回值通过回调方法返回
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

static napi_value ParamsTest(napi_env env, napi_callback_info info)
{
    size_t argc = 5;
    napi_value args[5] = {nullptr};
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
    
    //获取第一个参数--数字类型
    double a;
    napi_get_value_double(env, args[0], &a);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "double a: %{public}f", a);
    
    //获取第二个参数--字符串类型
    size_t typeLen = 0;
    napi_get_value_string_utf8(env, args[1], nullptr, 0, &typeLen);
    char *b = new char[typeLen + 1];
    napi_get_value_string_utf8(env, args[0], b, typeLen + 1, &typeLen);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "string b: %{public}s", b);
    
    //获取第三个参数--bool类型
    bool c;
    napi_get_value_bool(env, args[2], &c);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "boolean c: %{public}d", c);
    
    //第四个参数--数组类型
    // 检查参数是否为数组
    bool is_array;
    napi_is_array(env, args[3], &is_array);
    if (!is_array) {
        napi_throw_type_error(env, nullptr, "Argument must be an array");
        return nullptr;
    }
    // 获取数组长度
    uint32_t length;
    napi_get_array_length(env, args[3], &length);
    // 遍历数组
    for (int i = 0; i < length; i++) {
        napi_value result;
        napi_get_element(env, args[3], i, &result);
        
        size_t len = 0;
        napi_get_value_string_utf8(env, result, nullptr, 0, &len);
        char *text = new char[len + 1];
        napi_get_value_string_utf8(env, result, text, len + 1, &len);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "array d[%{public}d]: %{public}s", i, text);
    }
    
    //获取第五个参数--ArrayBuffer类型
    bool is_array_buffer;
    // 检查第五个参数是否为ArrayBuffer
    napi_is_arraybuffer(env, args[4], &is_array_buffer);
    if (is_array_buffer) {
        napi_value array_buffer_value;
        uint8_t *data;
        size_t byte_length;
        
        array_buffer_value = args[4];
        // 获取ArrayBuffer的外部数据指针和长度
        napi_get_arraybuffer_info(env, array_buffer_value, (void **)&data, &byte_length);

        OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "sizeof(uint8_t) = %{public}d", (int)sizeof(uint8_t));

        // 使用data指针处理ArrayBuffer数据
        OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "arraybuffer size = %{public}d,  (ie: bytes=%{public}d) ",
                     (int)(byte_length / sizeof(uint8_t)), (int)byte_length);

//         for (int i = 0; i < ((int)(byte_length / sizeof(uint8_t))); ++i) {
//             OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "data[%{public}d] = %{public}d ", i, *(data + i));
//         }
    } else {
        // 参数不是ArrayBuffer的处理逻辑
        OH_LOG_Print(LOG_APP, LOG_INFO, 0, "test", "e is not ArrayBuffer");
    }
    
    napi_value result = nullptr;
    napi_get_undefined(env, &result);
    return result;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        //第一个参数是index.d.ts定义的方法名,第三个参数是当前cpp文件中的方法名
        { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "addWithCallBack", nullptr, AddWithCallBack, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "paramsTest", nullptr, ParamsTest, 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);
}

ArtTS侧调用接口

直接修改默认创建的Index.ets

import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';//导入C++模块

@Entry
@Component
struct Index {
  callBack = (result:number)=>{
    hilog.info(0x0000, 'testTag', 'addWithCallBack(4,5) = %{public}d', result);
  }

  build() {
    Row() {
      Column({space:10}) {
        Button('add(2,3)')
          .onClick(()=>{
            hilog.info(0x0000, 'testTag', 'add(2,3) = %{public}d', testNapi.add(2, 3));
          })

        Button('addWithCallBack(4,5)')
          .onClick(()=>{
            hilog.info(0x0000, 'testTag', 'addWithCallBack(4,5) Begin');
            testNapi.addWithCallBack(4,5,this.callBack)
          })

        Button('paramsTest')
          .onClick(()=>{
            //获取一个arraybuffer数据,本地有什么图片资源就用哪个就行
            getContext().resourceManager.getMediaContent($r('app.media.app_icon')).then((mediaContent)=>{
              hilog.info(0x0000, 'testTag', 'paramsTest getMediaContent success');
              hilog.info(0x0000, 'testTag', 'paramsTest Begin');
              testNapi.paramsTest(4, "text", false, ['apple','boy','cat'],mediaContent.buffer)
            })
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值