HarmonyOS Next鸿蒙NDK使用示例

devtools/2024/9/19 14:06:53/ 标签: harmonyos, 华为, 鸿蒙, 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_Hclass 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_ENDstatic 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;// 检查第五个参数是否为ArrayBuffernapi_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_ENDstatic 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%')}
}


http://www.ppmy.cn/devtools/112709.html

相关文章

笔试强训day07

在字符串中找出连续最长的数字串 #include <bits/stdc.h>using namespace std; const int N 500; char s[N]; bool check(char c) {return c > 0 && c < 9; } int main() {scanf("%s", s);int l -1, r -1;int n strlen(s);int left 0, rig…

Spring Boot 常用注解

1. 基础 Spring 注解 Component 标记一个类作为 Spring IoC 容器的一个组件。Repository 标记一个 DAO 类&#xff0c;同时提供了异常转换机制。Service 标记业务逻辑层的服务类。Controller 标记一个 Web 层的控制器类。RestController 结合了 Controller 和 ResponseBody&am…

GO Govaluate

govaluate 是一个用于在 Go 语言中动态求值表达式的库。它允许你解析和评估字符串形式的表达式&#xff0c;这些表达式可以包含变量、函数以及逻辑、算术和比较操作。它非常适合在运行时处理复杂的逻辑规则和条件表达式&#xff0c;而不需要重新编译代码。 安装 govaluate go…

C语言自定义类型结构体(24)

文章目录 前言一、结构体类型的声明结构体回顾结构体的特殊声明结构体的自引用 二、结构体的内存对齐对齐规则为什么存在内存对齐&#xff1f;修改默认对齐数 三、结构体传参四、结构体实现位段什么是位段位段的内存分配位段的跨平台问题位段的应用位段使用的注意事项 总结 前言…

Linux学习-Ansible(一)

环境- Rocky-Linux8.6 安装部署Ansible # 安装ansible [rootharbor ansible]# dnf install -y ansible-core #查看安装信息 [rootharbor ansible]# ansible-doc --version ansible-doc [core 2.12.2]config file /root/ansible/ansible.cfgconfigured module search path […

动态规划---不相交的线

题目&#xff1a; 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足&#xff1a; nums1[i] nums2[j]且绘制的直线不与任何其他连线&#xff08;非水…

SQLITE3数据库实现信息的增删改查

#include <myhead.h> #include <sqlite3.h> typedef struct { int id; char name[20]; int age; int money; }woker; int callbake(void *arg,int n,char **a,char **b)//回调 输出查找到的工人信息 { for(int i 0;i<n;i) { …

[数据集][目标检测]汽车头部尾部检测数据集VOC+YOLO格式5319张3类别

数据集制作单位&#xff1a;未来自主研究中心(FIRC) 版权单位&#xff1a;未来自主研究中心(FIRC) 版权声明&#xff1a;数据集仅仅供个人使用&#xff0c;不得在未授权情况下挂淘宝、咸鱼等交易网站公开售卖,由此引发的法律责任需自行承担 数据集格式&#xff1a;Pascal VOC格…

Linux05

1.echo命令 echo是输出命令&#xff0c;类似printf 例如&#xff1a;echo "hello world"&#xff0c;输出hello world echo pwd&#xff0c;输出pwd的位置。是键盘上~ 2.重定向符> >> >指把左边内容覆盖到右边 echo hello world>test.txt >…

MATLAB在嵌入式系统设计中的最佳实践

嵌入式系统设计是一个复杂的过程&#xff0c;涉及硬件和软件的紧密集成。MATLAB提供了一套全面的解决方案&#xff0c;从算法开发到代码生成&#xff0c;再到硬件验证&#xff0c;极大地简化了这一过程。本文将探讨使用MATLAB进行嵌入式系统设计的最佳实践&#xff0c;包括模型…

Vue Router push方法的使用

Vue Router push方法的使用 this.$router.push 是 Vue Router 提供的一个方法,用于在 Vue.js 应用中进行编程式导航。它的作用是将用户导航到应用中的不同路由。 基本作用 this.$router.push 方法会在浏览器历史记录中添加一个新的记录,并导航到指定的路由。它的工作方式类…

深度学习中常见的损失函数

在机器学习和深度学习中&#xff0c;损失函数用于衡量模型预测值与真实值之间的差异。根据任务的类型&#xff08;如回归、分类等&#xff09;&#xff0c;可以使用不同的损失函数。下面列举了一些常见的损失函数&#xff1a; 1. 回归问题中的损失函数 回归任务的目标是预测连…

广播与组播,超时检测

目录 一.超时检测 必要性 超时检测的设置方法 1. 通过函数自带的参数设置 2. 通过设置套接字属性进行设置 3. alarm函数与sigaction函数结合 二.广播与组播&#xff08;broadcast & multicast&#xff09; 1. 广播&#xff08;udp&#xff09; 理论&#xff1a…

什么是外贸专用路由器?

一、外贸专用路由器的显著特点 全球兼容性 外贸专用路由器支持多种国际通信标准和频段&#xff0c;能够无缝连接不同国家和地区的网络&#xff0c;从而避免因地域限制导致的网络问题。这种全球兼容性确保了外贸企业在全球范围内的网络部署更加顺畅&#xff0c;让企业在任何角落…

对抗性EM用于变分深度学习:在低剂量PET和低剂量CT中的半监督图像质量增强应用|文献速递--Transformer架构在医学影像分析中的应用

Title 题目 Adversarial EM for variational deep learning: Application to semi-supervised image quality enhancement in low-dose PET and low-dose CT 对抗性EM用于变分深度学习&#xff1a;在低剂量PET和低剂量CT中的半监督图像质量增强应用 01 文献速递介绍 医学影…

『功能项目』战士的伤害型技能【45】

我们打开上一篇44战士职业平A怪物掉血的项目&#xff0c; 本章要做的事情是制作技能按钮&#xff0c;点鼠标点击时释放对范围内怪物的伤害技能 首先双击打开资源脚本下的Canvas预制体 制作技能栏 在资源商店中下载免费资源 - 技能图片 将技能图片拖拽至技能栏的Button按钮组件…

深入理解Python中的魔法参数 *args 和 **kwargs

在Python编程中&#xff0c;函数的灵活性是其强大之处之一。其中&#xff0c;*args 和 **kwargs 是实现函数参数可变性的重要工具。 无论我们是Python初学者还是经验丰富的开发者&#xff0c;充分理解这两个概念都有助于编写更加灵活、高效的代码。 本文将深入探讨*args和**kw…

1、常用的数据库、表操作

基本的建表和数据库拷贝操作。 一、数据定义语言DDL show databases; # 查看全部数据库 show create database db; # 查看数据库db create database db; # 创建数据库db drop database db; # 删除数据库db use db; # 使用数据库db基本…

动态规划(算法)---03.斐波那契数列模型_最小花费爬楼梯

题目链接&#xff1a; 746. 使用最小花费爬楼梯 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/min-cost-climbing-stairs/description/ 一、题目解析 题目&#xff1a; 解析&#xff1a; 题目说cost[i]为从某一个台阶向上爬的的费用&#xff0c;我们…

网络原理2-网络层与数据链路层

目录 网络层数据链路层 网络层 网络层做的工作&#xff1a; 1、地址管理–>IP地址 2、路由选择–>数据包传输的路径规划 网络层主要的协议就是IP协议 IP协议的报头结构&#xff1a; 4位版本&#xff1a; 有两个取值&#xff0c;4表示IPv4&#xff0c;6表示IPv6&am…