开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)
开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)
前言——系列介绍
本系列文章主要是记录笔者在鸿蒙南向的学习与工作中的知识点笔记记录,其中不止会针对鸿蒙中的学习问题进行思考与记录,也会对涉及到的一些嵌入式等其他领域知识,自我学习的心得进行记录。
本篇内容主要是黄同学最近在OpenHarmony 南向开发学习中对NAPI框架以及一些代码中的接口,异步实现等机制的学习。
MindMap

NAPI框架简介
Node.js 的 N-API
NAPI 其实是最早应该是来自node.js中的一个拓展库(也可以说是一整套API接口),叫Node-API,叫做N-API。是用来构建本地插件的API,将所有的nodejs底层数据结构黑盒化,封装成二进制接口,这样就可以实现不同版本的Node.js使用同样的接口,其目的是为了简化开发和维护。
NAPI (OpenHarmony)
- NAPI,全称
Native API,是OpenHarmony系统中的一套原生模块拓展开发框架,基于Nodejs中的N-API开发,为开发者提供了JS与C/C++不同语言模块之间的相互访问,交互的能力。它可以用于规范化封装IO、OS底层等,并可以提供相应的JS接口供开发者调用。当然。N-API也可以做到这一点。 - 区别于
N-API,主要在于NAPI针对OpenHarmony 系统做了 一些适配化和优化。但二者的目的都是为了简化和统一原生模块的开发和维护,提高跨平台和跨版本的兼容性。
JS和C/C++互相访问实现原理(浅谈)
鄙人浅谈一下这个东西,欢迎各位斧正!
- 不同的语言的数据类型采用的是
napi_value类型做封装和转换(计算机网络协议既视感),而像函数等接口则采用如napi_create_function()以及napi_call_function()等来进行创建和调用。 - 使用到了 V8 引擎,且对 V8的接口做了 黑盒化 和 抽象化,使得更加稳定。
Code Question
主要是记录一下在读以及编写Code时的遇到的问题的以及自己积累的心得体会。
大多是一些代码中的接口的解释和个人结合相关资料后的一点理解。
#ifdef __cplusplus extern “C”
- 这是一个在cpp中的宏命令,其表示的是如果在cpp文件中,我们需要调用一个C文件的接口
- 背景:
- C 和 C++ 对于函数名字处理的机制不同,众所周知,C++支持函数重载,因此在执行函数时会对名字有特殊处理,但是C不同,C认为函数名只是一个名字。
- 如果需要使用到C中写好的接口,需要使用C方式的链接,因此在需要
extern "C"来提示编译器在将cpp文件转为汇编时将该处对接口的调用方式由Cpp方式改为C方式,从而可以正确链接。
- 好处:就是方便了开发,使得Cpp对C的兼容性更强,对于已经写得很好的C接口,无需用Cpp再写一份。
_attribute_((constructor))
- 这是GCC一个特有的语法,用来修饰一个函数,从而让该函数在“main”之前执行,所以可以用来做初始化以及其他准备工作,比如初始化块变量或注册回调函数。可以避免一些依赖问题,提高性能。
- 相反,
__attribute__((destructor))可以修饰函数,使得这个函数在共享库卸载或者程序退出时执行。 - 这两个都是C++ 11 标准中引入的属性指定符序列中的一种,属性指定符序列是一种标准语法。
- 该语法还可以携带一个优先级参数,用于指定多个构造函数的执行顺序,优先级越低,执行越早。
- 区别
static- static 变量是在全局变量初始化后,main执行之前的,而
__attribute__((constructor))是在全局变量初始化之前执行,这样可以避免依赖问题。 - static只能在当前文件中使用,而
__attribute__((constructor))可以在不同文件或者动态链接库中使用。
- static 变量是在全局变量初始化后,main执行之前的,而
NAPI_CALL
- 是一个接口函数,用来调用JS中的函数,参数包括环境变量,接收对象,函数对象,参数个数,参数数组,返回值
- 使用场景
- 封装IO、CPU密集型、OS底层能力,并将JS接口对外暴露。
- 实现JS与C/C++代码的互相访问。
- 优先封装异步方法。
- 该函数与其他类型的接口函数的区别
- 这是一个宏,可以用来检测NAPI函数的返回值是否正确,其他类型的函数需要手动检测。
- 可以调用JS中任意函数,无论是全局还是对象的,其他接口只能调用特定类型以及特定范围的接口。
- 可以在任何地方需要回调的时候调用,不需要额外的参数以及DS。
- 优势
- 简化NAPI函数的调用和错误处理,提高Code的可读性和可维护性。
- 可以方便调用JS中的接口,实现C/C++和JS代码的互相访问
- 任意调用,无需额外的参数以及DS
- 局限性
- 宏,不能作为函数指针传递给其他函数
- 不能直接处理异步操作,需要结合其他接口
- 存在兼容性和稳定性问题。
DELCARE_NAPI_FUCTION
-
这是NAPI的一个宏,看名字大家都知道这个是用来声明一个函数的,黄同学在很多使用NAPI的Cpp代码都能看到这个宏。
-
宏定义原型(参数),有两种形式
// 不传回调 #define DECLARE_NAPI_FUNCTION(modname,name) // 传递回调 #define DECLARE_NAPI_FUNCTION_EX(modname, name, func) -
两种形式
- 传递三个参数(模块名,函数名,回调函数),这种形式最常见,由开发者定义回调函数的逻辑和返回值。
- 传递两个参数(模块名,函数名),这种形式其实是一种简化写法,会自动使用一个默认的回调函数,将JS传递的参数转换为C的数据类型,并将C函数的返回值转化为napi_value类型返回给JS。
- 在使用宏的时候,会根据传递参数的个数来执行,这种方式其实就是宏的条件编译,而不是 函数重载。
-
实现原理
- 生成一个napi_value类型的函数,调用到napi_create_function函数,创建一个JS对象,并将回调函数作为JS对象的内部数据。
- 将生成的函数添加到一个全局数组中,用于存储所有的NAPI模块接口函数。
- 框架初始化的时候,遍历这个数组,将每个接口导出到JS中,方便调用。
- JS调用接口时,NAPI框架会调出对应的回调函数,并将JS的参数和返回值转化为napi_value,实现JS和C/C++间的交互。
实现原理的流程图,不包括框架初始化
napi_get_cb_info
-
napi的一个函数,用于获取回调函数的参数和其他信息下面是它的原形
napi_status napi_get_cb_info(napi_env env, napi_callback_info cbinfo,size_t* argc, napi_value* argv,napi_value* this_arg, void** data);参数解释:
- env:环境变量
- cbinfo:回调函数信息
- argc:接收参数个数的指针
- argv:存放参数值的数组
- this_arg 是 JS中的this对象,data是接收数据指针的指针。
NAPI 函数定义限制
这是一条使用框架编写一些C/C++代码作为JS的接口的时需要注意的事情。
黄同学在做一个板子的sample的时候发现,某个smaple的样例源码无法跑通,除了一些简单的语法错误,最主要的是函数定义时的参数类型。
在本身数据是没有NAPI类型的数据的,但是框架中很多接口的定义都是用napi类型,所以我们在定义的时候,传递参数可以用void*,即空类型传递,然后在函数体内再对应修改即可。否则调用时会不符合NAPI中接口的定义。
异步实现机制
以下内容只讨论计算机方面,不要和我听异步电机啥的,黄同学表示考完控制后,看到电机这个东西真的头很大。
什么是异步
- 异步操作不需要等待结果返回,而同步操作则需要等待。
- 优缺点取决于应用场景
- 异步会提高效率和响应速度,但会增加复杂度和难度。
- 同步比较简单直接,缺点是会造成阻塞和资源浪费。
- 异步和多线程:
- 首先,这是两个东西,虽然黄同学在很多时候也会把这两个东西弄混,但是确实是有区别。
- 区别在于,多线程编程是异步机制的常见实现方式之一,但并不是唯一,所以我们在看很多异步的操作认为是多线程其实是没什么问题的。
在NAPI中,有两种实现异步操作:**Calllback **和 Promise。
Callback
- 就是一般的回调函数机制,相对来说比Promise这种代码逻辑比较复杂,类似常见算法中递归的过程,相对来说,代码的可读性比较差,你只需回想一下你第一次看递归代码的时候大概就知道这种过程了。
- 优点
- 是JS的原生特性,无需额外的库或抽象层。
- 可以在完成异步操作后执行一些操作。
- 缺点
- 代码的可读性和可维护性差,当多个回调嵌套时,就会出现传说中的 回调地狱。
- 对错误处理变得复杂,因为每个回调都需要检查错误并传递给下一个回调。
- 不能返回多个参数,只能返回一个对象(有些资料对这个的解释是Callback是以参数的形式返回结果,但是并不准确,参考JS官方文档以及javascript - How do you properly return multiple values from a Promise? - Stack Overflow等内容,所谓的参数应该是一个参数,或者说是参数就是对象)
- Code(JS)
// 定义一个异步的除法函数,接受两个数字和两个回调函数作为参数
function divisionAPI(number, divider, successCallback, errorCallback)
{if (divider == 0) {return errorCallback(new Error("Division by zero"));}successCallback(number / divider);
}// 调用异步的除法函数,传入两个数字和两个回调函数
divisionAPI(10, 2, function(result) {// 成功的回调函数,打印结果console.log("The result is " + result);
}, function(error) {// 失败的回调函数,打印错误console.error("Something went wrong: " + error.message);
});
Promise
- Promise 其实就是一种封装的异步操作结果的对象。
- 三种状态
pending,等待fulfilled,已成功rejected,已失败
- 优点:
- 可以采用同步的方式编写异步代码,因为异步操作结果已经被封装成了Promise,这样可以避免回调地狱。
- Promise对象本身是用链式而不是回调的方式调用,利用链式调用可以组合多个异步操作,并且可以用
then或者catch操作来处理异步操作结果成功或者失败的情况。 - 比较灵活,可以用
all方法等待所有异步操作的完成,也可以用race方法来获取最先完成的异步操作的结果。
- 缺点
- 因为做了一定程度的封装,用对象来存储异步操作的结果,其实就会消耗一些额外的内存和性能。
- 违背异步非阻塞I/O的原则,因为需要等待异步操作的完成(当从pending变到fulfilled或者rejected时,这个过程是不可逆的,使用
await关键字在async函数中,等待一个Promise对象,实际过程就是再那个时间段代码调用到异步操作,此时async不执行,有点类似于同步操作,或者说我们一开始学习编程时最简单的函数调用,详见asynchronous - Why use promise or async/await on child processes in Node.js? - Stack Overflow 和 How to use promises - Learn web development | MDN (mozilla.org))。 - 也是只能返回一个对象。
- Code(JS)
// 定义一个异步的除法函数,返回一个Promise对象
function divisionAPI(number, divider) {return new Promise(function(resolve, reject) {if (divider == 0) {return reject(new Error("Division by zero"));}resolve(number / divider);});
}// 调用异步的除法函数,返回一个Promise对象
divisionAPI(10, 2).then(function(result) {// 成功的回调函数,打印结果console.log("The result is " + result);}).catch(function(error) {// 失败的回调函数,打印错误console.error("Something went wrong: " + error.message);});
参考资料
黄同学写这篇blog的一些参考资料,有需要的可以看看。
- OpenHarmony 源码解析之NAPI框架内部实现分析-51CTO.COM
- [三方库移植之NAPI开发1]—Hello OpenHarmony NAPI - 知乎 (zhihu.com)
- ace_napi: Development framework for extending the JS Native Module | 原生模块扩展开发框架 (gitee.com)
- vendor_unionman: 该仓库托管广东九联科技股份有限公司厂商驱动及配置文件 - Gitee.com
- 写一个N-API没那么难? - 知乎 (zhihu.com)
- 为JS写C++扩展,Napi第一步_napi.h_番茄V王子的博客-CSDN博客
- c++ - How to save an asynchronous callback for later using node-addon-api / napi - Stack Overflow
- 三方库移植之NAPI开发–异步调用:Callback&Promise(四)-51CTO.COM
- 箭头函数表达式 - JavaScript |多核 (mozilla.org)
- What are the pros and cons of using promises instead of callbacks? - Best Interview Question
- asynchronous - Why use promise or async/await on child processes in Node.js? - Stack Overflow
- How to use promises - Learn web development | MDN (mozilla.org)
相关文章:
开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)
开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一) 前言——系列介绍 本系列文章主要是记录笔者在鸿蒙南向的学习与工作中的知识点笔记记录,其中不止会针对鸿蒙中的学习问题进行思考与记录,也会对涉及到的一些嵌入式等其他领域知识&…...
Spring - Spring框架概述面试题总结
文章目录01. 什么是Spring?02. Spring框架的设计目标,设计理念,和核心是什么?03. Spring的优点是什么?04. Spring框架中都用到了哪些设计模式?05. Spring有哪些应用场景?06. Spring由哪些模块组成…...
学习python好就业么
Python的普及与数据挖掘、人工智能和数值计算等领域的蓬勃发展相关,但同时也与普遍编程需求的增加有关。 Python作为人工智能的头号语言,一方面会吸引大量计划从事人工智能的人来学习,另一方面自然也带动了网络上对这门“新语言”的关注和讨…...
瑞幸咖啡的最终目标并不是做国内市场大哥
出品 | 何玺 排版 | 叶媛 日前,瑞幸咖啡发布2022年第四季度及全年财报。数据显示,在刚刚过去的2022年,瑞幸咖啡首次实现了营收超百亿,门店规模也超越老对手星巴克,成为了国内第一连锁咖啡品牌。 那么,瑞幸…...
GPT 模型介绍 | GPT3 / GPT3.5 + Flask | Github源码链接
1. 模型介绍 Chatgpt 使用与 InstructGPT相同的方法,使用来自人类反馈的强化学习 (RLHF) 来训练该模型,但数据收集设置略有不同。我们使用监督微调训练了一个初始模型:人类 AI 训练员提供对话,他们在对话中扮演双方——用户和 AI…...
蓝桥杯入门即劝退(二十六)组合问题(回溯算法)
-----持续更新Spring入门系列文章----- 如果你也喜欢Java和算法,欢迎订阅专栏共同学习交流! 你的点赞、关注、评论、是我创作的动力! -------希望我的文章对你有所帮助-------- 专栏:蓝桥杯系列 一、题目描述 给定两个整数 n …...
现代卷积神经网络(ResNet)
专栏:神经网络复现目录 本章介绍的是现代神经网络的结构和复现,包括深度卷积神经网络(AlexNet),VGG,NiN,GoogleNet,残差网络(ResNet),稠密连接网络…...
PTA:L1-019 谁先倒、L1-020 帅到没朋友、L1-021 重要的话说三遍(C++)
目录 L1-019 谁先倒 问题描述: L1-020 帅到没朋友 问题描述: 实现代码(只过了部分): L1-021 重要的话说三遍 问题描述: 实现代码: 无解析 L1-019 谁先倒 问题描述: 划拳是…...
STL常见容器之set/multiset、map/multimap
set/multiset—集合容器 特点 所有元素都会在插入时自动被排序 本质 set/multiset属于关联式容器,底层结构是二叉树实现 set和multiset区别 set不可以插入重复数据,而multiset可以set插入数据的同时会返回插入结果,表示插入是否成功multiset…...
ThreadLocal 实现原理
每个 Thread 中都存储着一个成员变量:ThreadLocalMap /** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals null; ThreadLocal 本…...
BUUCTF [羊城杯 2020]easyre 题解
一.查壳 64位无壳 二.主函数逻辑 可以得知flag长度为38,然后进行三次加密 第一次加密是base64加密,得到code1 第二次加密是将code1拆成四段赋给code2 第三次加密是将code2内的数字和字母移3位,其他字符不变 str2保存的是最终的加密字符 三.encode_one_base64 看到主函数…...
网络协议(十二):HTTPS(SSL/TLS、TLS1.2的连接)
网络协议系列文章 网络协议(一):基本概念、计算机之间的连接方式 网络协议(二):MAC地址、IP地址、子网掩码、子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类、ISP、上网方式、公网私网、NAT 网络…...
九九乘法表--课后程序(Python程序开发案例教程-黑马程序员编著-第3章-课后作业)
实例9:九九乘法表 乘法口诀是中国古代筹算中进行乘法、除法、开方等运算的基本计算规则,沿用至今已有两千多年。古代的乘法口诀与现在使用的乘法口诀顺序相反,自上而下从“九九八十一”开始到“一一如一”为止,因此,古…...
在超算上安装文件树命令tree
超算平台使用的centos系统没有内置tree命令,需要通过源码安装。记录安装流程如下。 1. 下载源码包 下载链接如下: http://mama.indstate.edu/users/ice/tree/ 选择“Download the latest version” 如本文下载了源码包“tree-2.1.0.tgz”. 2. 源码包…...
论文投稿指南——中文核心期刊推荐(经济管理)
【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…...
在vue中如果computed属性是一个异步操作怎么办?
在计算属性中使用异步方法时,可以使用async/await来处理异步操作。由于计算属性是基于它们的依赖缓存的,所以我们需要使用一个返回Promise的异步方法来确保计算属性能够正常运行。 下面是一个简单的示例,演示如何在计算属性中使用异步方法&am…...
SRP合批问题
1)SRP合批问题 2)多个Base相机渲染到同一个渲染目标,移动平台花屏的问题 3)粒子系统对GPU Instancing的支持 4)如何修改URP下场景和UI分辨率分离(不需要改颜色空间) 这是第327篇UWA技术知识分…...
蓝牙5.1低功耗SOC 私有协议2.4GHz芯片HS6621
HS6621CxC是一个优化功耗真正芯片系统(SOC)解决方案,适用于蓝牙低功耗和私有的2.4GHz应用场景。它集成了一个高性能、小功率的射频收发器,具有蓝牙基带和丰富的外围IO扩展。还集成了电源管理,以提供高效的电源管理。 …...
数据库连接池
数据库连接---执行完毕---释放 连接--释放 十分浪费系统资源 池化技术:准备一些预先的资源,过来就连接预先准备好的 最小连接数: 10 最大连接数:15 业务最高承载上限 排队等待, 等待超时:100…...
Arrays-sort-的用法
1.集合交换元素 Collections.swap(List<?> list, int i, int j); 源码: /*** Swaps the elements at the specified positions in the specified list.* (If the specified positions are equal, invoking this method …...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...
