当前位置: 首页 > news >正文

3【鸿蒙/OpenHarmony/NDK】如何在鸿蒙应用中使用NDK?

各位码友们好!今天这篇干货主要聚焦实操细节,希望能帮大家少踩坑。​
要是过程中遇到哪块没看懂、有疑问,或者你有更优的实现思路,评论区尽管聊!发现文档里有疏漏或错误也尽管指出来 ——
技术这东西就得互相挑刺才能越磨越精,咱们一起把这些知识点吃透~

前置学习

  • 在【鸿蒙/OpenHarmony/NDK】C/C++开发教程之环境搭建我们学习了如何搭建NDK开发环境,同时我们运行了我们的第一个使用了NDK的应用。
  • 在【鸿蒙/OpenHarmony/NDK】什么是NDK? 为啥要用NDK?我们知道了NDK的概念和用途。
  • 这篇文章我们详细来看下我们之前运行的第一个使用了NDK的应用到底是如何实现的,我们一行一行代码来学习。

Hellow World详细解读

我们从上到下来看下NDK是如何被引入到应用中的。

import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';const DOMAIN = 0x0000;@Entry
@Component
struct Index {@State message: string = 'Hello World';build() {Row() {Column() {Text(this.message).fontSize($r('app.float.page_text_font_size')).fontWeight(FontWeight.Bold).onClick(() => {this.message = 'Welcome';hilog.info(DOMAIN, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));})}.width('100%')}.height('100%')}
}

在ets中调用通过NDK实现的js接口

我们来看,如何在ets侧调用通过NDK实现的js接口。

  • 首先import testNapi from 'libentry.so';这句是将我们js接口所在的so导进来,方便后续调用里面的接口。
  • testNapi.add(2, 3)这句就是调用我们通过NDK实现的js接口addadd方法的返回值是number类型,返回2加3的结果5。

如何调用NDK实现js接口的我们已经讨论清楚了。下面我们在进一步看下,DevEco是如何知道我们的libentry.so中到底包含了哪些方法的?

  • 如图1,我们在DevEco里面输入testNapi.的时候它能够自动联想出add方法,DevEco到底是如何知道libentry.so中有add方法的呢?
    图1自动联想:
    在这里插入图片描述
  • 如图2,我们发现有一个src/main/cpp/types/libentry/Index.d.ts文件,它里面声明了add方法。那么DevEco是怎么知道我们libentry.so中的方法声明是在src/main/cpp/types/libentry/Index.d.ts文件中的呢?
    图2方法声明文件:
    在这里插入图片描述
  • 如图3,在项目的配置文件oh-package.json5里面,声明了一个依赖,说我这个项目依赖libentry.so,和libentry.so相关的配置在./src/main/cpp/types/libentry目录下。
  • ./src/main/cpp/types/libentry目录下存在同名的一个配置文件oh-package.json5,里面定义了libentry.so的信息,其中就包括so对应的类型声明文件Index.d.ts
    图3项目配置文件:
    在这里插入图片描述
    总结一下,DevEco通过项目的配置文件oh-package.json5知道了这个项目依赖 ./src/main/cpp/types/libentry目录下的libentry.so。然后通过./src/main/cpp/types/libentry目录下的oh-package.json5配置文件知道了libentry.so相关的接口声明是放在Index.d.ts文件中的。这样DevEco读取Index.d.ts文件就能够知道libentry.so里面包含哪些接口了,就能够做自动联想了。

使用C++实现js接口

前面我们知道了如何在ets侧调用js接口,接下来我们看下被调用的add方法在C++侧是如何实现的?

#include "napi/native_api.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;}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);
}

注册napi模块

  • RegisterEntryModule函数作为so的构造函数,在so被加载成功后被调用。
  • 函数里面调用了napi_module_register方法,这是告诉系统说我这里有一个napi模块,模块信息在demoModule里面。
  • 通过demoModule的定义我们可以看到,这个模块的名字叫做entry,它的初始化函数是Init方法。
  • Init方法里面,首先定义了一个napi_property_descriptor类型的变量desc,里面有一个"add"字符串,这个代表js侧的方法名字。还有一个Add符号,这个就是定义在这个文件中的napi_value Add(napi_env env, napi_callback_info info)函数。这个意思是说,在调用js侧的add方法的时候,实际会调用到c++侧的napi_value Add(napi_env env, napi_callback_info info)方法。
  • 然后通过napi_define_properties将上述信息注册到js环境中。

解析js接口的参数

接着我们看C++侧的Add方法的实现,他是如何解析js侧传过来的参数,又是如何将计算结果传回到js侧的。

参数数解析

通过napi_get_cb_info获取JavaScript传入的参数信息:

  • 声明argc=2表示预期接收两个参数
  • args数组用于存储实际传入的参数值
  • 后两个nullptr表示不关心this对象和函数本身
类型检查

使用napi_typeof检查参数类型:

  • 分别对args[0]args[1]进行类型判断
  • 结果存储在valuetype0valuetype1变量中
数值转换

通过napi_get_value_double提取数值:

  • 将JavaScript的Number类型转换为C的double类型
  • 转换后的值存储在value0value1变量中
运算与返回

通过napi_create_double创建结果:

  • 将两个double值相加
  • 构造新的napi_value类型结果
  • 最终返回给JavaScript环境
http://www.dtcms.com/a/359632.html

相关文章:

  • 线阵相机和镜头选型案例介绍
  • 【不懂就问】-手机相关学习
  • 打开多个Excel文件后快速关闭所有的文档,并且退出Excel应用
  • Docker一小时快速上手(附报错解决方式)
  • 并发编程——11 并发容器(Map、List、Set)实战及其原理分析
  • deep seek的对话记录如何导出
  • CICD实战(1) - 使用Arbess+GitPuk+Docker快速实现项目打包构建、docker部署
  • 视频理解与行为识别全景综述
  • 计算机网络:(十六)TCP 的运输连接管理
  • 传统数据库out啦!KINGBASE ES V9R1C10 开启国产数据库“修仙”新纪元!
  • Redis六大常见命令详解:从set/get到过期策略的全方位解析
  • 大模型推理技术解析
  • AI热点周报(8.24~8.30):Grok 2.5开源,OpenAI Realtime正式商用,Meta或与OpenAI或Google合作?
  • 学习记录(二十二)--Overleaf中生成的PDF左上角1.5em问题
  • 【stm32】对射式红外传感器计次以及旋转编码器计次
  • 基于单片机智能大棚/温室大棚/智慧农业/智能栽培种植系统/温湿度控制
  • 使用VBA实现快速多重数据筛选
  • Flink部署实战:从入门到优化
  • 第 14 篇:K-Means与聚类思维——当AI在没有“标准答案”的世界里寻宝
  • python实现滤波器的简单案例
  • python如何打开显示svg图片
  • 阿里云-应用实时监控服务 ARMS
  • Unity笔记(九)——画线功能Linerenderer、范围检测、射线检测
  • AFSIM仿真脚本生成(三)脚本解析技术加速验证过程
  • Linux 系统都有哪些
  • HikariCP vs DBCP2 vs Tomcat JDBC:多场景数据库连接池方案对比与实践指南
  • 大模型RAG项目实战:Milvus向量数据库
  • 《SVA断言系统学习之路》【02】并发断言
  • C++11语言(三)
  • 读书笔记共享平台|基于SpringBoot的设计与实现