详解安卓辅助功能服务AccessibilityService(无障碍服务,微信抢红包助手原理)

news2024/9/24 5:23:33

前言

在手机的更多设置或者高级设置中,我们会发现有个无障碍的功能,很多人不知道这个功能具体是干嘛的,包括我们开发也很少接触这部分功能,以至于对这块不甚了解。前段时间在同事的安利下去了解了下这部分功能。在这里和大家浅谈下自己对这个功能的理解和部分运用。这边打算从 “是什么,为什么,怎么用,好不好”几个方面来说

提纲

是什么(应用场景,定义,作用)

为什么(原理及源码解析)

怎么用(如何开发无障碍服务)

怎么防(防止无障碍服务外挂的一些做法)

是什么(定位及作用)

为了更好的介绍辅助功能服务,这里先看下应用场景

辅助功能服务一般用于操作自动化和辅助操作

应用场景

● 操作自动化,通过辅助功能服务来代替用户执行连续性的操作,重复性的操作,或者特殊场景的操作(例如自动抢红包,自动点赞,自动回复,自动搜索更优惠商品)

WechatHelper   https://github.com/coder-pig/WechatHelper

● 辅助操作,帮助无法和设备完全交互的用户(例如患有视力问题或正在忙而无法操作手机的用户)执行操作(例如talkback(视力低弱辅助),随选听读,语音操作)

官方定义(AccessibilityService)

无障碍服务是一种应用程序,给有残疾的用户或暂时无法与设备完全交互的用户提供了更好的无障碍用户交互功能。比如驾驶、照顾小孩或者在吵闹的派对上可能需要额外或者替代的交互反馈。

Android提供标准的辅助功能服务,包括TalkBack,开发人员可以创建和分发自己的服务。

引入及发展

在Android 4.0以前,Accessibility功能单一,仅能过单向获取窗口信息(获取输入框内容);

在Android 4.0及以后,Accessibility增加了与窗口元素的双向交互,可以操作窗口元素(点击按钮)。

在Android 4.0之前,无障碍服务事件在提供有关用户选择的用户界面控件的信息时,只提供了有限的上下文信息。在许多情况下,缺少的上下文信息可能对理解所选控件的含义至关重要。

Android 4.0通过基于视图层次结构组合可访问性事件,显着扩展了辅助功能服务可以获得的有关用户界面交互的信息量。

辅助功能服务可以代表用户执行操作,包括更改输入焦点和选择(激活)用户界面元素。在Android

4.1(API级别16)中,操作范围已扩展为包括滚动列表和与文本字段交互。

辅助功能服务的作用

因为无障碍服务具有强大的界面监听能力和替代用户操作的能力,谷歌建议辅助功能服务仅应用于帮助残障用户使用Android设备和应用。

在我们开发者看来,无障碍服务显然能做的更多,例如微信抢红包应用,自动点赞,自动回复等

android 辅助功能google官方示例 https://github.com/googlesamples/android-BasicAccessibility

为什么(原理及源码解析)

Q:为什么辅助功能可以监听用户的操作,界面变化,并根据需要进行反馈

A:辅助功能通过在后台中运行无障碍服务,通过AccessibilityEvent接收指定事件的回调,这样的事件表示用户在界面中的一些状态转换,例如:焦点改变了,一个按钮被点击,等等。

简单的说无障碍就是一个后台监控服务,当你监控的内容发生改变时,就会调用后台服务的回调方法

从具体实例入手看原理:程序内部的后台服务—— 内部的跨进程通信 AM & AMS

拿一个具体的例子来看,这是一个抢红包的外挂,把WeChat称作Target

APP,就是被监控的APP,当跳出来一个红包,触发了一个AccessibilityEvent,system_server中的AccessibilityManagerService将AccessibilityEvent分发给有AccessibilityService的APP,称为Accessibility

APP,这个AccessibilityService受到这个AccessibilityEvent后,会找到这个页面的Open

Button,模拟点击。


而在程序内部,这个过程其实就是三个类之间的交互 AccessibilityManager(AM):发送AccessibilityEventAccessibilityManagerService(AMS):分发事件AccessibilityService(AS):进行回应 

看到AccessibilityManagerService这样的起名,就很容易联想到,这是一个Binder通信的过程,AM通过IAccessibilityManager(AMS的本地Binder)与AMS跨进程通信。AMS通过IAccessibilityManagerClient(AM的本地Binder)与AM通信。

AM是与AMS进行通信

AM是什么时候与AMS进行通信的,查看源码可以知道,AM的设计其实是一个单例模式,每个app进程都会有一个AM,而AM在构建的时候,即getInstance的时候,就会调用tryConnectToServiceLocked()的方法,连接AMS,得到AMS的代理后,把自己的代理也设置给AMS,这样AM就可以和AMS进行通信了


AS和AMS联系的时机

那么AS又是什么时候和AMS有联系的呢,这又是一个跨进程binder通信的过程。

无障碍服务是很强大的服务,需要我们进到设置中开启这个服务。绑定Service。

这里我们结合时序图进行说明。

Settings->Accessibility->enable(enableAccessibilityServiceLocked())

Settings->Accessibility->disable(disableAccessibilityServiceLocked())

Some RegisterBroadcastReceivers (registerBroadcastReceivers())

当用户在设置->无障碍里面选择了开启或关闭一个辅助功能,会导致一些系统状态会变化;Accessibility APP的安装状态会以BroadcastReceivers的方式会通知状态改变;还有其他的一些状态改变。这些变化最终会调用到AMS的onUserStateChangedLocked()方法。

RegisterBroadcastReceivers  很多情况简单列为这四种,安装app,更新app。强制关闭app,删除app。

onUserStateChangedLocked方法中,有比较多的方法调用,都是一些特定状态的更新,但我们这次只用关注updateServicesLocked这个方法,是处理无障碍服务绑定的

updateServicesLocked这个方法涉及到多个AMS类内部的集合,遍历如上图左边所见

这个方法会遍历ams中的mInstalledServices,看名字可以知道是已经安装的无障碍服务列表。

然后根据enableServices,判断是否已经启用,如果启用则通过mComponentNameToServiceMap判断是否为空,为空就会new一个AccessibilityServiceConnection, 调用其bindlock方法,绑定

未启用就通过mComponentNameToServiceMap判断是否为空,不为空就调用其unbindLocked(),解绑

接下来的过程其实就是跨进程的Binder通信。AS会通过onBind(Intent intent)这个函数返回一个IAccessibilityServiceClientWrapper对象给AccessibilityServiceConnection,这个对象就是AS的本地Binder,AccessibilityServiceConnection通过这个本地Binder去和AS通信。

然后AccessibilityServiceConnection会在onServiceConnected中调用方法,把自己的代理传到AS中。

监听无障碍服务事件(触发 分发 回调)

讲完服务是什么时候绑定的,怎么建立通信的。接下来说下怎么监听事件的,AccessibilityEvent 是从哪里传递出来的

这里分为触发 分发 回调三个部分

先说触发,以点击为例

首先需要知道View都有接入 AccessibilityEventSource接口 分有两个方法。用于发送无障碍事件。

回归到图的部分

● View.java -- performClick() 被点击调用

● View.java -- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 发送类型为click的事件

● View.java -- sendAccessibilityEventInternal() 创建了一个AM的实例(app单例),判断是否enable状态,创建AM实例时会通过binder绑定远程AMS

● View.java -- sendAccessibilityEventUnchecked(AccessibilityEvent) 这个Event方法是根据type调用AccessibilityEvent.obtain(type)创建的

● View.java -- sendAccessibilityEventUncheckedInternal() 会调用onInitializeAccessibilityEvent()初始化一些event信息,比如className/packageName/source等,然后会调用getParent().requestSendAccessibilityEvent(this,event)将event分发给ParentView。requestSendAccessibilityEvent会不断往上调用getParent().requestSendAccessibilityEvent(this,event),ViewRootImpl.requestSendAccessibilityEvent不管ParentView是哪一个,最终会到View层次中的顶层,也就是ViewRootImpl

● ViewRootImpl -- requestSendAccessibilityEvent() 在这里就会调用AccessibilityManager.sendAccessibilityEvent(event);

● AccessibilityManager -- sendAccessibilityEvent(event); 获取远程的AMS 通过getServiceLocked()获取本地Binder,然后通过service.sendAccessibilityEvent(dispatchedEvent, userId)去调用AMS的sendAccessibilityEvent方法。

然后是分发,接着刚才的AccessibilityManagerService.java-- sendAccessibilityEvent()说

● AccessibilityManagerService.java --sendAccessibilityEvent()

● AccessibilityManagerService.java --notifyAccessibilityServicesDelayedLocked(),AMS会维护一个绑定AS的List(mBoundServices),List中每一个AccessibilityServiceConnection对应一个绑定的AS,因此遍历mBoundServices,获取其中的serviceconection 然后去到AccessibilityServiceConnection的notifyAccessibilityEvent()函数。

● AbstractAccessibilityServiceConnection.java-- notifyAccessibilityEvent()

● AbstractAccessibilityServiceConnection.java-- notifyAccessibilityEventInternal()   在notifyAccessibilityEventInternal()中,listener是AS的本地Binder(IAccessibilityServiceClient类型),最终是回调到了AS的onAccessibilityEvent()。到这里Dispatch的部分就结束了。

剩下回调,回调没什么好说的,就是调用方法。

而onAccessibilityEvent()方法是继承了AccessibilityService必须重写的。

原理和源码解析的结论

1.辅助功能通过在后台中运行无障碍服务,通过AccessibilityEvent接收指定事件的回调

2.Accessibility服务框架类似于hook在Android View组件树中的一套实现,它并不是独立的一套机制,而是”寄生”在View的显示、事件分发的流程中。

● 功能实现依赖于ViewRootImpl, ViewGroup, View视图层级管理的基本架构。在视图变化时发出事件、当收到视图操作请求时也能够作出响应。

● system_server在实现该功能的过程中扮演着中间人的角色。当被监听APP视图变化时,APP首先会发出事件到system_server,随后再中转到监听者APP端。当监听者APP想要执行视图操作时,也是首先在system_server中找到对应的客户端binder proxy,再调用相应接口调用到被监听APP中。完成相关操作后,通过已经获取到的监听APP binder proxy句柄,直接binder call到对应的监听客户端。

3.无障碍权限十分重要,切记不可滥用,APP自身也需要有足够的安全意识,防止恶意应用通过该服务获取用户隐私信息

怎么用(如何开发无障碍服务)

刚刚说到,为什么辅助功能可以监听用户的操作,界面变化,并根据需要进行反馈,那怎么使用呢,我们继续探究

一、创建服务类

继承AccessibilityService 类,重写onServiceConnected()方法、onAccessibilityEvent()方法和onInterrupt()方法

● onServiceConnected (可选)系统成功绑定该服务时被触发,也就是当你在设置中开启相应的服务,系统成功的绑定了该服务时会触发,通常我们可以在这里做一些初始化操作

● onUnbind(Intent intent) (可选)系统要关闭该服务是,将调用此方法。主要用来释放资源。

● onAccessibilityEvent(AccessibilityEvent event) 有关AccessibilityEvent事件的回调函数,系统通过sendAccessibiliyEvent()不断的发送AccessibilityEvent到此处

● onInterrupt 系统需要中断AccessibilityService反馈时,将调用此方法。AccessibilityService反馈包括服务发起的震动、音频等行为。

二、声明服务

像其他Service服务一样,需要在AndroidManifest.xml中声明该服务.除此之外,该服务还必须配置以下几项,否则都会使该服务没有反应:

 ●配置<intent-filter>,固定的action:android.accessibilityservice.AccessibilityService

 ●声明BIND_ACCESSIBILITY_SERVICE权限,以便系统能够绑定该服务(4.0版本后要求)

 ●android:label:在无障碍列表中显示该服务的名字

三、配置服务参数

配置用来接受指定类型的事件,监听指定package,检索窗口内容,获取事件类型的时间等等。其配置服务参数有两种方法:

 ● 方法一:安卓4.0之后可以通过meta-data标签指定xml文件进行配置

accessibilityEventTypes:表示该服务对界面中的哪些变化感兴趣,即哪些事件通知,比如窗口打开,滑动,焦点变化,长按等。具体的值可以在AccessibilityEvent类中查到,如typeAllMask表示接受所有的事件通知

accessibilityFeedbackType:表示反馈方式,比如是语音播放,还是震动

canRetrieveWindowContent:表示该服务能否访问活动窗口中的内容。也就是如果你希望在服务中获取窗体内容,则需要设置其值为true

description:对该无障碍功能的描述

notificationTimeout:接受事件的时间间隔,通常将其设置为100即可

packageNames:表示对该服务是用来监听哪个包的产生的事件

 ●  方法二:通过代码动态配置参数(setServiceInfo(AccessibilityServiceInfo))

AccessibilityServiceInfo类被用于配置AccessibilityService信息,该类中包含了大量用于配置的常量字段及用来xml属性,常见的有:accessibilityEventTypes,canRequestFilterKeyEvents,packageNames等等

https://developer.android.google.cn/reference/android/accessibilityservice/AccessibilityServiceInfo

https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo

https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityWindowInfo

https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityEvent

四、启动服务

在设置->辅助功能中便可以找到我们的服务.该服务默认处在关闭状态,需要手动开启.

也可以在app中跳转到设置页。

五、处理事件信息

onAccessibilityEvent(AccessibilityEvent event)是该服务的核心方法,其中参数event封装来自界面相关事件的信息,比如我们可以获得该事件的事件类型,进而根据起类型选择不同的处理方式:

常见种类如图

六、获取节点信息

获取了界面窗口变化后,这个时候就要获取控件的节点。整个窗口的节点本质是个树结构,通过以下操作节点信息

获取窗口节点(根节点)

AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

获取指定子节点(控件节点)

//通过文本找到对应的节点集合

List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text);

//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd

List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(clickId);

AccessibilityService 获取View的Id

获取id的一些方法

● layoutInspector

● Uiautomator viewer

随缘,各种原因,成功率很低。截图失败或者超时

● hierarchy view

相对可靠。

可以在命令行工具中,执行如下命令 得到相关文件

adb shell uiautomator dump

执行成功 系统会返回 UI hierchary dumped to: /mnt/sdcard/window_dump.xml

当然 这个文件地址也是可以更改的。 adb shell uiautomator dump [file]

七、模拟节点点击

当我们获取了节点信息之后,对控件节点进行模拟点击、长按等操作,AccessibilityNodeInfo类提供了performAction()方法让我们执行模拟操作,具体操作可看官方文档介绍,这里列举常用的操作

//模拟点击

accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);

//模拟长按

accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);

//模拟获取焦点

accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);

//模拟粘贴

accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);

八、示例及代码——自动安装app

接下来用例子说明下怎么使用无障碍服务

平时我们点击安装包进行安装的时候,会弹出界面让我们确认。一般来说我们就是点击下一步,安装,然后安装完成后点击打开或者完成。

如果用无障碍服务的话,是可以在点击安装包的时候直接自动帮忙点了下一步,安装之类的。

具体做法就是在 onAccessibilityEvent()中,判断是否安装程序进程,然后进行下相应的点击动作,代码如右图所示 这边用的手机是小米,所以是小米的packgeInstaller的判断。如果是的话,就开始执行找控件点击的逻辑

小作业试验:完成自动回复的功能

首先我们知道一般软件,如微信接收信息时是有通知的,显示在通知栏中,所以我们的事件监听需要监听通知栏变化

通知的点击打开是可以进入到相应的聊天页面的。

如果想在聊天页面实现回复,就是在输入框中输入文字,然后点击发送就可以,剩下的回调由微信的程序完成

可以简单实现下,如果真的想通过无障碍服务实现自动回复,逻辑会严谨很多,首先判断依据需要跳转聊天页的依据就不会这么简单。然后是场景也有多种区分,如后台,息屏,跳转失败,代理清除通知等

回复的逻辑也会复杂很多,接入半自动回复机器人接口不用说,还有查找输入框控件失败,群聊,多条信息,是否需要执行自动回复的逻辑判断等

怎么防(防止无障碍服务外挂的一些做法)

通过原理和源码查看,我们知道了无障碍服务是内嵌到整个android的view层类里的,但是还是有缺陷的

如通过Onclick实现的点击事件,可以捕捉到,但通过onTouchEvent实现的的点击事件,捕捉不到

如有获取节点文字的方法,但没有提供获取图片数据的方法,导致外挂抢票实现验证码输入不太可能。

那么我们可以怎么防御呢,有以下一些做法

AccessibilityManager有提供获取安装了的无障碍服务和开启的无障碍的服务的方法。从而让我们可以知道用户有哪些无障碍服务,及运行的服务有哪些。帮助我们确定自动点击的外挂来源,从而在打开外挂后有一个警告

了解AccessibilityServices源码之后,我们知道其内部核心原理就是调用TextView的findViewsWithText方法。只要复写这个方法,就可以屏蔽文案检查

AccessibilityServices执行点击事件最终在调用View的mOnClickListener。可以利用onTouch代替onClick屏蔽服务调用点击的动作

某些微信红包插件会监控Notification的弹出,那么我们是否可以随意发送这样的Event出来,从而混干扰外挂插件的运行逻辑。这种做法大部分情况比较鸡肋,也看外挂同学的逻辑严不严谨

这个其实是继第一种方式后的操作,在收集好已知外挂的信息后,设立黑名单,遍历系统内部所有已安装的app,鉴别package name 和app name。执行相应处理

参考资料

Ø官方介绍

https://developer.android.com/guide/topics/ui/accessibility/services

Øandroid 辅助功能google官方示例

https://github.com/googlesamples/android-BasicAccessibility

ØWechatHelper 微信助手

https://github.com/coder-pig/WechatHelper

ØAccessibilityService分析与防御

https://lizhaoxuan.github.io/2018/01/27/AccessibilityService%E5%88%86%E6%9E%90%E4%B8%8E%E9%98%B2%E5%BE%A1/

最后编辑于:2020-05-22 10:45


喜欢的朋友记得点赞、收藏、关注哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2053667.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

scikit-learn特征预处理

特征预处理 什么是特征预处理 通过一些转换函数&#xff0c;将特征数据转换成更适合算法模型的特征数据的过程 数值数据的无量纲化&#xff1a; 归一化标准化 特征预处理API sklearn.preprocessing为什么进行无量纲化 通过欧式距离公式计算两个约会对象是否属于同一类别 …

使用HTML和cgi实现网页登录功能

0.HTML文件结构 一.HTML文件 1.test.html <!DOCTYPE html> <html><head><meta charset"utf-8"><title>菜鸟教程(runoob.com)</title></head><body><!-- 将结果提交给/cgi-bin/test.cgi下 --><form actio…

联盟推广计划:释放SaaS企业增长潜力

在SaaS行业&#xff0c;用户增长是企业成功的关键。本文深入探讨联盟推广计划&#xff0c;分析其核心特点和优势&#xff0c;以及如何实施这一策略以实现用户增长和品牌扩展。随着SaaS市场的不断成熟&#xff0c;企业越来越需要创新的营销策略来突破增长瓶颈。PartnerShare联盟…

2025舜宇内推码

舜宇光学集团校招 【2025内推码】 DSwNQ9yu DSwNQ9yu DSJXN8Mr 舜宇光学科技2025校招内推&#xff01;冲冲冲&#xff01; 光学龙头-舜宇集团2025届全球校园招聘正式启动&#xff01;&#xff01;&#xff01; 提供住宿&#xff08;硕士单人间&#xff0c;独立卫浴&#xff0…

【大数据平台】性能优化与成本控制

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

multimodel ocr dataset

InternLM-XComposer2-4KHD InternLM-XComposer2-4KHD a light-weight Vision Encoder OpenAI ViT-Large/14Large Language Model InternLM2-7B, 这篇论文采用的是一种动态分辨率的输入&#xff1b; 全图有一个global view,resize到336*336&#xff1b; 然后把图片resize再pad…

PointPillars算法解析

说明 本篇主要对基于LIDAR的3D目标检测算法PointPillars算法论文进行解析。 论文地址&#xff1a;https://arxiv.org/pdf/1812.05784.pdf 代码地址&#xff1a;https://github.com/open-mmlab/OpenPCDet 参考链接1&#xff1a;https://zhuanlan.zhihu.com/p/357626425 参考链接…

探索数据结构:红黑树的分析与实现

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 红黑树的介绍 1.1. 红黑树的引入 我们前面学习了AVL树&#xff0c;…

re正则模块

正则是一个十分重要且基础的模块 学习正则模块就要了解正则的一些基本字符 正则的基本方法有很多 但是大体上分为三种匹配 分割 替换 匹配有match search fullmatch findall finditer 注意finditer得到结果是一个可迭代类型需要遍历才能得到结果 使用group方法就可以查看返回…

【安全】XSS

文章目录 xss1.反射型XSS Payload的一些情况010203040506070809101112131415 HTML文档处理过程0x01 HTML解析0x02 URL解析0x03 JavaScript 解析 2.DOM型Ma Spaghet!JefffUgandan KnucklesRicardo MilosAh Thats HawtLigmaMafia 3.存储型 xss 用户的输入没有进行很好的过滤&…

对比新旧两个数据库表之间的差异

ServerDatabaseVersionUpdateHelper 一个对比不同数据库之间表数据差异的开源软件&#xff0c;欢迎大家到github上点赞 应用下载地址 功能介绍 对比表结构差异和表数据之间的差异 并根据查询生成新的更新sql语句 使用 1. 填写新旧数据库配置 server数据库地址;port数据库端…

报错:xx in xx cannot be applied to ‘()‘ @Data注解的无参构造方法不生效(原因及解决办法)

问题描述 创建User类时&#xff0c;添加了Data注解和User的构造方法 import lombok.Data;Data public class User {private Long id;private String name;private Integer age;private String email;public User(Long id, String name, Integer age, String email) {this.id …

机器学习--常见算法总结

有监督学习算法 1. 线性回归算法 概念&#xff1a;线性回归是一种统计方法&#xff0c;用于预测一个变量&#xff08;因变量&#xff09;与一个或多个自变量&#xff08;特征变量&#xff09;之间的关系。目标是通过线性方程建立自变量和因变量之间的关系模型。 作用&#x…

vertical-align: bottom;

问: 这个弹框中, "张三" 文字在某些ios手机中会上升到顶部, 图片也会移动, 西方二维码也会向下移动, 请问什么原因? 回答: 我们在 "张三" 这个元素dt上, 加上了vertical-align: bottom;这个属性, 让这个在顶部的元素在最下面, 就解决了样式错乱的问题.

SCC-F 23212-0-110310控制器abb面价

SCC-F 23212-0-110310控制器面价 SCC-F 23212-0-110310控制器面价 SCC-F 23212-0-110310控制器面价 SCC-F 23212-0-110310控制模块接线图 SCC-F 23212-0-110310控制模块电路图 SCC-F 23212-0-110310控制模块线路图 SCC-F 23212-0-110310伺服电机控制器是数控系统及其他相…

【C语言】最详细的单链表(两遍包会!)

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:C语言数据结构_小米里的大麦的博客-CSDN博客 &#x1f381;代码托管:黄灿灿/数据结构 (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、前言 二、单链表的概念 1. 单链表的特点 2. 单链表的基本…

Aqua使用记录

Java Kotlin Groovy Python 建议使用Poetry环境 Poetry executable&#xff1a;/Users/wan/Library/Application Support/pypoetry/venv/bin/poetry 安装依赖包 poetry add package 或者在.toml文件添加依赖包信息 Selenium with Python Selenium 生成html测试报告&#x…

Linux驱动——杂项驱动GPIO子系统

一&#xff1a;内核层框架 在介绍linux驱动之前先介绍一下系统。 系统分为两层&#xff1a; 1.系统层 2.内核层 对于内核层就要说一下其中的内核层运行的框架了 代码如下&#xff1a; //头文件 #include "linux/kernel.h" #include "linux/module.h" …

git-版本管理工具基本操作-创建仓库-拉取-推送-暂存库-版本库

1、创建仓库 2、克隆仓库到本地&#xff08;首次拉取需要输入用户名和密码&#xff0c;用户名用邮箱&#xff0c;密码用登录gitee的密码&#xff0c;后面配置密钥后可以直接clone&#xff09; 在命令行输出两行指令配置git才能克隆&#xff1a; username&#xff1a;gitee账号…

2D Inpainting 与NeRF 3D重建的多视角一致性问题

一 问题&#xff1a; NeRF依赖于输入图像的一致性。NeRF&#xff08;Neural Radiance Fields&#xff09;在生成三维场景时&#xff0c;依赖于从多个视角拍摄的输入图像之间的一致性来准确地推断场景的三维结构和颜色信息。 具体来说&#xff1a; 多视角一致性&#xff1a; Ne…