当前位置: 首页 > 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.xdnf.cn/news/1409779.html

相关文章:

  • Makefile语句解析:头文件目录自动发现与包含标志生成
  • 【读论文】自监督消除高光谱成像中的非独立噪声
  • AI 取代部分岗位后:哪些职业更易被替代?人类该如何提升 “不可替代性”?
  • 硬件-电感学习DAY6——电感磁芯损耗全解析
  • 多人协作开发指南二
  • GPU-Driven Rendering inAssassin’s Creed Mirage
  • Android开发简介
  • LangChain框架深度解析:定位、架构、设计逻辑与优化方向
  • 计算机视觉与深度学习 | 双目立体特征提取与匹配算法综述——理论基础、OpenCV实践与MATLAB实现指南
  • leetcode_240 搜索二维矩阵 II
  • leetcode-hot-100(堆)
  • 分享一个实用的B站工具箱(支持音视频下载等功能)
  • Conda相关的用法
  • 业务逻辑漏洞类型及防范措施
  • 在实践中学Java(中)面向对象
  • 当 AI 开始 “筛选” 信息:算法偏见会加剧认知鸿沟吗?如何构建公平的 AI 生态?
  • 【算法笔记】算法归纳整理
  • (LeetCode 每日一题) 36. 有效的数独 (数组、哈希表)
  • 基于多模态大模型的PCB智能缺陷检测与分析
  • 人工智能学习:机器学习相关面试题(一)
  • 进程状态 —— Linux内核(Kernel)
  • 【动态规划】回文串问题
  • Wend看源码-marker(RAG工程-PDF文件解析)
  • R notes[2]
  • 鸿蒙Next文本组件全解析:输入框、富文本与属性字符串开发指南
  • Caffeine TimerWheel时间轮 深度解析:O(1)复杂度增删和触发时间事件
  • 李宏毅NLP-13-Vocoder
  • html添加水印
  • 2025年- H103-Lc211--3090. 每个字符最多出现两次的最长子字符串(双指针)--Java版
  • leetcode 268 丢失的数字