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

开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)

开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)

前言——系列介绍

本系列文章主要是记录笔者在鸿蒙南向的学习与工作中的知识点笔记记录,其中不止会针对鸿蒙中的学习问题进行思考与记录,也会对涉及到的一些嵌入式等其他领域知识,自我学习的心得进行记录。

本篇内容主要是黄同学最近在OpenHarmony 南向开发学习中对NAPI框架以及一些代码中的接口,异步实现等机制的学习。

MindMap

在这里插入图片描述

NAPI框架简介

Node.js 的 N-API

NAPI 其实是最早应该是来自node.js中的一个拓展库(也可以说是一整套API接口),叫Node-API,叫做N-API。是用来构建本地插件的API,将所有的nodejs底层数据结构黑盒化,封装成二进制接口,这样就可以实现不同版本的Node.js使用同样的接口,其目的是为了简化开发和维护。

NAPI (OpenHarmony)

  1. NAPI,全称 Native API,是OpenHarmony系统中的一套原生模块拓展开发框架,基于Nodejs中的N-API开发,为开发者提供了JS与C/C++不同语言模块之间的相互访问,交互的能力。它可以用于规范化封装IO、OS底层等,并可以提供相应的JS接口供开发者调用。当然。N-API也可以做到这一点。
  2. 区别于 N-API主要在于NAPI针对OpenHarmony 系统做了 一些适配化和优化。但二者的目的都是为了简化和统一原生模块的开发和维护,提高跨平台和跨版本的兼容性。

JS和C/C++互相访问实现原理(浅谈)

鄙人浅谈一下这个东西,欢迎各位斧正!

  1. 不同的语言的数据类型采用的是 napi_value类型做封装和转换(计算机网络协议既视感),而像函数等接口则采用如 napi_create_function() 以及 napi_call_function() 等来进行创建和调用。
  2. 使用到了 V8 引擎,且对 V8的接口做了 黑盒化抽象化,使得更加稳定。

Code Question

主要是记录一下在读以及编写Code时的遇到的问题的以及自己积累的心得体会。

大多是一些代码中的接口的解释和个人结合相关资料后的一点理解。

#ifdef __cplusplus extern “C”

  1. 这是一个在cpp中的宏命令,其表示的是如果在cpp文件中,我们需要调用一个C文件的接口
  2. 背景:
    1. C 和 C++ 对于函数名字处理的机制不同,众所周知,C++支持函数重载,因此在执行函数时会对名字有特殊处理,但是C不同,C认为函数名只是一个名字。
    2. 如果需要使用到C中写好的接口,需要使用C方式的链接,因此在需要 extern "C" 来提示编译器在将cpp文件转为汇编时将该处对接口的调用方式由Cpp方式改为C方式,从而可以正确链接。
  3. 好处:就是方便了开发,使得Cpp对C的兼容性更强,对于已经写得很好的C接口,无需用Cpp再写一份。

_attribute_((constructor))

  1. 这是GCC一个特有的语法,用来修饰一个函数,从而让该函数在“main”之前执行,所以可以用来做初始化以及其他准备工作,比如初始化块变量或注册回调函数。可以避免一些依赖问题,提高性能。
  2. 相反,__attribute__((destructor))可以修饰函数,使得这个函数在共享库卸载或者程序退出时执行。
  3. 这两个都是C++ 11 标准中引入的属性指定符序列中的一种,属性指定符序列是一种标准语法。
  4. 该语法还可以携带一个优先级参数,用于指定多个构造函数的执行顺序,优先级越低,执行越早。
  5. 区别static
    1. static 变量是在全局变量初始化后,main执行之前的,而__attribute__((constructor))是在全局变量初始化之前执行,这样可以避免依赖问题。
    2. static只能在当前文件中使用,而__attribute__((constructor))可以在不同文件或者动态链接库中使用。

NAPI_CALL

  1. 是一个接口函数,用来调用JS中的函数,参数包括环境变量,接收对象,函数对象,参数个数,参数数组,返回值
  2. 使用场景
    1. 封装IO、CPU密集型、OS底层能力,并将JS接口对外暴露。
    2. 实现JS与C/C++代码的互相访问。
    3. 优先封装异步方法。
  3. 该函数与其他类型的接口函数的区别
    1. 这是一个宏,可以用来检测NAPI函数的返回值是否正确,其他类型的函数需要手动检测。
    2. 可以调用JS中任意函数,无论是全局还是对象的,其他接口只能调用特定类型以及特定范围的接口。
    3. 可以在任何地方需要回调的时候调用,不需要额外的参数以及DS。
  4. 优势
    1. 简化NAPI函数的调用和错误处理,提高Code的可读性和可维护性。
    2. 可以方便调用JS中的接口,实现C/C++和JS代码的互相访问
    3. 任意调用,无需额外的参数以及DS
  5. 局限性
    1. 宏,不能作为函数指针传递给其他函数
    2. 不能直接处理异步操作,需要结合其他接口
    3. 存在兼容性和稳定性问题。

DELCARE_NAPI_FUCTION

  1. 这是NAPI的一个宏,看名字大家都知道这个是用来声明一个函数的,黄同学在很多使用NAPI的Cpp代码都能看到这个宏。

  2. 宏定义原型(参数),有两种形式

    // 不传回调
    #define DECLARE_NAPI_FUNCTION(modname,name)
    // 传递回调
    #define DECLARE_NAPI_FUNCTION_EX(modname, name, func) 
    
  3. 两种形式

    1. 传递三个参数(模块名,函数名,回调函数),这种形式最常见,由开发者定义回调函数的逻辑和返回值。
    2. 传递两个参数(模块名,函数名),这种形式其实是一种简化写法,会自动使用一个默认的回调函数,将JS传递的参数转换为C的数据类型,并将C函数的返回值转化为napi_value类型返回给JS。
    3. 在使用宏的时候,会根据传递参数的个数来执行,这种方式其实就是宏的条件编译,而不是 函数重载
  4. 实现原理

    1. 生成一个napi_value类型的函数,调用到napi_create_function函数,创建一个JS对象,并将回调函数作为JS对象的内部数据。
    2. 将生成的函数添加到一个全局数组中,用于存储所有的NAPI模块接口函数。
    3. 框架初始化的时候,遍历这个数组,将每个接口导出到JS中,方便调用。
    4. JS调用接口时,NAPI框架会调出对应的回调函数,并将JS的参数和返回值转化为napi_value,实现JS和C/C++间的交互。

实现原理的流程图,不包括框架初始化

在这里插入图片描述

napi_get_cb_info

  1. 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);
    

    参数解释:

    1. env:环境变量
    2. cbinfo:回调函数信息
    3. argc:接收参数个数的指针
    4. argv:存放参数值的数组
    5. this_arg 是 JS中的this对象,data是接收数据指针的指针。

NAPI 函数定义限制

这是一条使用框架编写一些C/C++代码作为JS的接口的时需要注意的事情。

黄同学在做一个板子的sample的时候发现,某个smaple的样例源码无法跑通,除了一些简单的语法错误,最主要的是函数定义时的参数类型。

在本身数据是没有NAPI类型的数据的,但是框架中很多接口的定义都是用napi类型,所以我们在定义的时候,传递参数可以用void*,即空类型传递,然后在函数体内再对应修改即可。否则调用时会不符合NAPI中接口的定义。

异步实现机制

以下内容只讨论计算机方面,不要和我听异步电机啥的,黄同学表示考完控制后,看到电机这个东西真的头很大。

什么是异步

  1. 异步操作不需要等待结果返回,而同步操作则需要等待。
  2. 优缺点取决于应用场景
    1. 异步会提高效率和响应速度,但会增加复杂度和难度。
    2. 同步比较简单直接,缺点是会造成阻塞和资源浪费。
  3. 异步和多线程:
    1. 首先,这是两个东西,虽然黄同学在很多时候也会把这两个东西弄混,但是确实是有区别。
    2. 区别在于,多线程编程是异步机制的常见实现方式之一,但并不是唯一,所以我们在看很多异步的操作认为是多线程其实是没什么问题的。

NAPI中,有两种实现异步操作:**Calllback **和 Promise

Callback

  1. 就是一般的回调函数机制,相对来说比Promise这种代码逻辑比较复杂,类似常见算法中递归的过程,相对来说,代码的可读性比较差,你只需回想一下你第一次看递归代码的时候大概就知道这种过程了。
  2. 优点
    1. 是JS的原生特性,无需额外的库或抽象层。
    2. 可以在完成异步操作后执行一些操作。
  3. 缺点
    1. 代码的可读性和可维护性差,当多个回调嵌套时,就会出现传说中的 回调地狱
    2. 对错误处理变得复杂,因为每个回调都需要检查错误并传递给下一个回调。
    3. 不能返回多个参数,只能返回一个对象(有些资料对这个的解释是Callback是以参数的形式返回结果,但是并不准确,参考JS官方文档以及javascript - How do you properly return multiple values from a Promise? - Stack Overflow等内容,所谓的参数应该是一个参数,或者说是参数就是对象)
  4. 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

  1. Promise 其实就是一种封装的异步操作结果的对象。
  2. 三种状态
    1. pending,等待
    2. fulfilled,已成功
    3. rejected,已失败
  3. 优点:
    1. 可以采用同步的方式编写异步代码,因为异步操作结果已经被封装成了Promise,这样可以避免回调地狱。
    2. Promise对象本身是用链式而不是回调的方式调用,利用链式调用可以组合多个异步操作,并且可以用then 或者 catch操作来处理异步操作结果成功或者失败的情况。
    3. 比较灵活,可以用all方法等待所有异步操作的完成,也可以用race方法来获取最先完成的异步操作的结果。
  4. 缺点
    1. 因为做了一定程度的封装,用对象来存储异步操作的结果,其实就会消耗一些额外的内存和性能。
    2. 违背异步非阻塞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))。
    3. 也是只能返回一个对象。
  5. 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的一些参考资料,有需要的可以看看。

  1. OpenHarmony 源码解析之NAPI框架内部实现分析-51CTO.COM
  2. [三方库移植之NAPI开发1]—Hello OpenHarmony NAPI - 知乎 (zhihu.com)
  3. ace_napi: Development framework for extending the JS Native Module | 原生模块扩展开发框架 (gitee.com)
  4. vendor_unionman: 该仓库托管广东九联科技股份有限公司厂商驱动及配置文件 - Gitee.com
  5. 写一个N-API没那么难? - 知乎 (zhihu.com)
  6. 为JS写C++扩展,Napi第一步_napi.h_番茄V王子的博客-CSDN博客
  7. c++ - How to save an asynchronous callback for later using node-addon-api / napi - Stack Overflow
  8. 三方库移植之NAPI开发–异步调用:Callback&Promise(四)-51CTO.COM
  9. 箭头函数表达式 - JavaScript |多核 (mozilla.org)
  10. What are the pros and cons of using promises instead of callbacks? - Best Interview Question
  11. asynchronous - Why use promise or async/await on child processes in Node.js? - Stack Overflow
  12. 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由哪些模块组成&#xf…...

学习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&#xff08;List<?> list, int i, int j&#xff09;&#xff1b; 源码&#xff1a; /*** Swaps the elements at the specified positions in the specified list.* (If the specified positions are equal, invoking this method …...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...