AccessibilityEvent的生成和处理

在 Android 框架层,AccessibilityEvent 的生成和处理是通过系统的 UI 框架和辅助功能服务框架密切协作来实现的。这个机制涉及几个关键的部分:UI 组件、辅助功能服务、事件监听和事件分发。以下是对这些部分和它们如何协同工作的详细解释:

1 UI框架与事件生成

Android 的 UI 框架允许应用构建交互界面。每个 UI 元素(如按钮、文本视图等)都是 View 类的一个实例。当这些视图发生状态改变(如被点击、被滑动等)时,UI 框架负责生成相应的 AccessibilityEvent

  • 事件触发:当用户与设备上的视图交互(如通过触摸屏点击或滑动)时,Android 的输入处理系统会捕捉这些操作并将它们转换为相应的事件(如触摸事件)。这些事件随后被传递给相应的视图进行处理。
  • 事件处理:视图通过其事件监听器(如 OnClickListenerOnTouchListener 等)响应这些事件。在事件处理过程中,视图可以调用 sendAccessibilityEvent(int eventType) 方法来生成相应类型的 AccessibilityEvent

Android 的事件分发系统处理从顶层窗口到具体被点击视图的事件传递。当一个触摸事件发生时,系统首先将事件传递给顶级视图,然后通过 dispatchTouchEvent() 方法沿视图树向下传递到具体的子视图。在视图层级中,每个视图都可以重写 dispatchTouchEvent 方法来检测和响应触摸事件。如果该视图或其子视图消费了事件(例如,一个按钮识别并响应了点击事件),dispatchTouchEvent 将返回 true。否则,事件将继续传递给视图层级中的其他视图。如下的 onClick 方法中已包含如何发送辅助功能事件的代码。当按钮被点击时,除了响应点击外,还调用了 sendAccessibilityEvent 来发送一个类型为 TYPE_VIEW_CLICKED 的辅助功能事件。

Button button = findViewById(R.id.my_button);
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 处理点击事件Toast.makeText(v.getContext(), "Button clicked!", Toast.LENGTH_SHORT).show();// 发送辅助功能事件v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);}
});

2 AccessibilityEvent 的处理和分发

  • 事件创建:当调用 sendAccessibilityEvent() 方法时,视图对象会构建一个 AccessibilityEvent,包含事件类型、触发事件的视图信息、视图内容描述、当前状态等信息。
  • 事件分发:一旦 AccessibilityEvent 被创建,它会被发送到 AccessibilityManager,这是一个系统服务,负责管理所有活动的辅助功能服务。
  • 辅助功能服务AccessibilityManager 将事件分发给所有注册并启用的辅助功能服务。这些服务可以通过实现 AccessibilityService 类并重写 onAccessibilityEvent() 方法来接收和处理这些事件。Android 系统提供了 AccessibilityManager,这是一个系统级服务,作为桥梁在应用的 UI 组件和辅助功能服务之间传递信息。
  • 这个服务负责:
  • 接收来自应用视图的 AccessibilityEvent
  • 管理辅助功能服务的注册和激活状态。
  • AccessibilityEvent 分发给所有激活的辅助功能服务。

3 获取事件相关的信息

在 Android 的辅助功能服务中,有两种主要方式来获取与点击事件相关的信息:通过 event.getText() 和通过 event.getSource() 获取的 AccessibilityNodeInfo。在实际开发中,选择哪种方法取决于具体需求和性能考量。对于简单的文本获取,使用 event.getText() 可能更合适;而对于需要详细操作视图或获取更多视图信息的场景,则 AccessibilityNodeInfo 是更好的选择。例如在微信聊天界面的顶部昵称点击事件中,需要使用event.getText()才会获取到该文本信息。

3.1 使用 event.getText()

这种方法直接从 AccessibilityEvent 对象中获取与事件相关的文本列表。这种方法简单直接,但可能不提供完整的上下文信息。

示例代码:
@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();switch (eventType) {case AccessibilityEvent.TYPE_VIEW_CLICKED:Log.i(TAG, "TYPE_VIEW_CLICKED");// 使用event.getText()获取与点击事件关联的文本信息List<CharSequence> texts = event.getText();String eventText = texts.isEmpty() ? "" : texts.get(0).toString(); // 获取第一项文本,假设它是主要文本Log.i(TAG, "Clicked text: " + eventText);break;}}

3.2 使用 AccessibilityNodeInfo

这种方法通过 event.getSource() 获取一个 AccessibilityNodeInfo 对象,该对象代表触发事件的UI组件。AccessibilityNodeInfo 提供了丰富的信息和操作接口。提供了关于触发事件视图的详细信息,包括视图的状态、属性等。允许进行交互,如点击、长按等操作。 可以遍历视图树,获取更多关联视图的信息。需要更多的处理和可能的资源回收。对性能要求稍高,因为涉及到对象的创建和回收。

示例代码:
@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();switch (eventType) {case AccessibilityEvent.TYPE_VIEW_CLICKED:Log.i(TAG, "TYPE_VIEW_CLICKED");// 获取事件关联的AccessibilityNodeInfo对象AccessibilityNodeInfo nodeInfo = event.getSource();if (nodeInfo != null) {// 从节点信息中获取文本CharSequence text = nodeInfo.getText();String eventText = text != null ? text.toString() : "";Log.i(TAG, "Clicked text: " + eventText);// 重要:记得回收nodeInfo对象nodeInfo.recycle();}break;}}

4 自动化UI Automator依靠 Accessibility API 实现

在 Android UI Automator 中主要依靠 Accessibility API 来实现。这个过程涉及到几个关键的技术步骤,包括获取根节点、遍历节点、查询节点属性和执行操作。以下是这些步骤的具体实现细节和原理:

4.1 获取根节点

首先,测试框架通过 AccessibilityService 获取当前活动窗口的根 AccessibilityNodeInfo 对象。这是遍历 UI 树的起点。

在 Android 的 UI Automator 框架中,UiSelector 是一个强大的工具,用于创建针对特定 UI 元素的查询。这些查询可以基于多种属性,如 ID、文本、内容描述、类名等。在底层,UI Automator 的 UiSelector 通过以下几个步骤实现与 AccessibilityService 的交互和 UI 树的遍历:

a. 获取 AccessibilityNodeInfo 根节点

首先,UI Automator 从辅助功能服务获取当前活动窗口的根节点。

AccessibilityNodeInfo rootNode = UiAutomatorBridge.getInstance().getRootInActiveWindow();

这个方法调用返回的 rootNode 是当前显示的窗口的顶层视图节点。

b. 遍历和匹配

然后,UiSelector 使用指定的条件(例如文本、类名等)对 AccessibilityNodeInfo 树进行深度优先遍历,查找符合条件的节点。

List<AccessibilityNodeInfo> matchNodes = rootNode.findAccessibilityNodeInfosByViewId("com.example:id/button_submit");

这个方法查找所有具有特定视图 ID 的节点。类似的方法还有 findAccessibilityNodeInfosByTextfindAccessibilityNodeInfosByClassName,它们分别用于按文本和类名进行搜索。

c. 执行操作

一旦找到匹配的节点,UI Automator 可以在这些节点上执行各种操作,如点击、输入文本等。

for (AccessibilityNodeInfo node : matchNodes) {if (node.isClickable()) {node.performAction(AccessibilityNodeInfo.ACTION_CLICK);}
}

4.2 递归遍历 UI 树

UI Automator 使用递归方法遍历整个 UI 树,可以获取或操作界面上的每一个元素。每个节点都代表屏幕上的一个 UI 元素,如按钮、文本字段等。

底层代码示例:
public void traverseTree(AccessibilityNodeInfo node) {if (node == null) return;// 处理当前节点,例如获取节点的某些属性int childCount = node.getChildCount();for (int i = 0; i < childCount; i++) {AccessibilityNodeInfo childNode = node.getChild(i);traverseTree(childNode);  // 递归调用以遍历子节点childNode.recycle();  // 回收节点信息以避免内存泄漏}
}

此方法遍历每个节点及其所有子节点,这是一种深度优先搜索(DFS)的遍历方式。

4.3 查询节点属性

在遍历的过程中,测试脚本可以查询每个节点的各种属性,例如文本内容、内容描述、类名等,以便识别需要操作的特定元素。

底层代码示例:
String text = node.getText() != null ? node.getText().toString() : null;
String className = node.getClassName().toString();

4.4 执行用户交互操作

UI Automator 允许执行各种用户交互操作,如点击、滑动等。这些操作是通过在相应的 AccessibilityNodeInfo 上调用操作方法实现的。

底层代码示例:
if (node.getText() != null && node.getText().toString().equals("Click Me")) {node.performAction(AccessibilityNodeInfo.ACTION_CLICK);  // 执行点击操作
}

4.5 使用选择器

UI Automator 提供了强大的选择器(UiSelector),允许测试脚本以声明式方式查找 UI 元素。选择器可以基于元素的各种属性配置,如 ID、文本内容、类型等。

底层代码示例:
UiObject button = new UiSelector().text("Submit").className("android.widget.Button").makeUiObject();
button.click();

UiSelector 允许测试脚本以非常灵活的方式指定元素的选择条件。例如:

UiObject button = new UiObject(new UiSelector().className("android.widget.Button").text("Submit"));
button.click();

这段代码创建了一个 UiSelector,用于查找文本为 “Submit” 的按钮,并在找到后执行点击操作。

5 AccessibilityNodeInfo树的构建

当应用的 UI 发生变化时(例如用户界面元素的添加、移动或更新),窗口管理系统将这些变化通知给辅助功能服务。
辅助功能服务使用这些信息来构建或更新一个辅助功能节点信息树(即 AccessibilityNodeInfo 树),这个信息树反映了当前活动窗口的 UI 结构。
每个节点(AccessibilityNodeInfo)在树中代表屏幕上的一个 UI 元素,例如按钮、标签、图像等。
当调用 getRootInActiveWindow() 方法时,辅助功能服务从窗口管理系统查询当前活动窗口的根节点。
这个根节点是 UI 树的顶级节点,从这个节点开始,可以遍历整个树,访问窗口中的所有 UI 元素。

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

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

相关文章

httprunner接口自动化测试框架使用说明【保姆级教程】

背景介绍&#xff1a; httprunner是国内开源的一个接口自动化框架&#xff0c;已经有部分公司开始使用这种框架来完成自己公司的接口自动化编写&#xff0c;本文主要是从简单的流程上去讲解咋使用的&#xff08;PS&#xff1a;开发者本尊的官网教程写的是真的烂。。。&#xf…

JVM调优实战

如果老年代能回收掉大部分&#xff0c;说明年轻代太小了&#xff0c;放不下 OOM 1数据量一次性申请的内存过多&#xff0c;比如数据库查询返回值大多&#xff0c;所以做个分页 2.并发过高的情况下&#xff0c;一些连接未释放 3.堆内存不够

DP-Kmaens密度峰值聚类算法

我有个问题 关于 [密度值>密度阈值] 的判定这里&#xff0c;新进来的新数据怎么确定他的密度值&#xff1f;密度阈值又是怎样确定的呢&#xff1f;

正则表达式 0.1v

正则表达式 扩展 --> :% s/\///g //文件里面所有的 / 去掉 * 通配符 \ //转义&#xff0c;让字符变成原本的意思 ^ //行首 $ //行尾 [0-9] //数字 [a-z] //小写字母 [A-Z] //大写字母 把文件的小写字母替换为大写字母&#xff1f; 固定写法 :% s/[a-…

Vscode git 插件

超好用的git记录 软件 安装之后&#xff0c;鼠标在哪一行就可以看最新一次是谁提交的&#xff0c;真的超好用&#xff01;&#xff01;&#xff01;

43页 | 2024年企业级BI平台白皮书(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 2024年企业级BI平台白皮书 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 诚挚邀请您微信扫码加入以下方案驿站知识星球&#xff0c;获取上万份PPT/WORD解决方案&#xff01;&#xff01;&…

【NOI】C++程序结构入门之循环结构二-for循环

文章目录 前言一、for循环1.导入2.语法3.使用场景4.条件控制5.小结 二、例题讲解问题&#xff1a;1264 - 4位反序数问题&#xff1a;1085 - 寻找雷劈数问题&#xff1a;1057 - 能被5整除且至少有一位数字是5的所有整数的个数问题&#xff1a;1392 - 回文偶数&#xff1f;问题&a…

Linux命令 netstat -anp | grep 的用法

文章目录 1、第一种解释2、第二种解释3、第三种解释4、第四种解释5、第五种解释6、netstat --help 在Windows中&#xff0c;杀死端口占用的博客链接 1、第一种解释 在Unix和Linux系统中&#xff0c;netstat -anp 命令用于显示所有的网络连接&#xff08; -a 表示所有&#xff…

文件md5加密

使用场景&#xff1a;为了避免上传资源空间的浪费&#xff0c;通过对文件进行md5摘要加密获取唯一的值&#xff0c;从数据库中查询是否已有该md5码存在&#xff0c;不存在的就上传&#xff0c;存在的话使用之前已存储的文件信息。 如何加密 下载插件browser-md5-file 【之前有…

maridb10.4.30数据库数据迁移

1.新建数据存储文件夹&#xff0c;例如E:\maridb_data 2.修改原数据所在目录的my.ini文件&#xff0c;例如D:\Program Files\MariaDB 10.4\data\my.ini 3.剪切除my.ini文件外的其他所有文件到迁移目的地文件(E:\maridb_data) 结果如下&#xff1a; 原数据文件目录&#xff1a…

聊聊限流的一些事儿

一、背景 最近几年&#xff0c;随着微服务的流行&#xff0c;服务与服务之间依赖越来越强&#xff0c;调用也越来越复杂&#xff0c;服务间的稳定性变突显出来。特别是在遇到突发请求时&#xff0c;常常需要通过缓存、限流、熔断降级、负载均衡等多种方式保证服务的稳定性。其…

C++命名空间(详解)

C基础语法 C基于C语言的改进&#xff1a;c在C语言的基础上引入并扩充了面向对象的概念 C基础概念&#xff1a;C是基于C语言而产生的,它即可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计 在1998年 出现C98…

爱普生差分晶振在光模块中的重要角色

光模块是现代通信设备中的重要组成部分&#xff0c;主要用于实现光电转换和信号传输&#xff0c;它是一种将光信号转换为电信号&#xff0c;或者将电信号转换为光信号的设备。在光纤通信中&#xff0c;光模块扮演着至关重要的角色。 光模块的主要组成部分包括光源、光接收器、…

OSPF学习笔记(状态机)

1、邻居关系 OSPF设备启动后&#xff0c;会通过OSPF接口向外发送Hello报文&#xff0c;收到Hello报文的OSPF设备会检查报文中所定义的参数&#xff0c;如果双方一致就会形成邻居关系&#xff0c;两端设备互为邻居 2、邻接关系 形成邻居关系后&#xff0c;如果两端设备成功交…

【代码随想录】【算法训练营】【第27天】 [39]组合总和 [40] 组合总和II [131]分割回文串

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day26&#xff0c; 休息的周末~ day 27&#xff0c;周一&#xff0c;库存没了&#xff0c;哭死~ 题目详情 [39] 组合总和 题目描述 39 组合总和 解题思路 前提&#xff1a;组合的子集问题&…

C# :IQueryable IEnumerable

文章目录 1. IEnumerable2. IQueryable3. LINQ to SQL4. IEnumerable & IQueryable4.1 Expression4.2 Provider 1. IEnumerable namespace System.Collections: public interface IEnumerable {public IEnumerator GetEnumerator (); }public interface IEnumerator {pubi…

气泡式水位计施工技术要求

1、气泡式水位计压力气管出气口应安装并固定在最低水位处&#xff0c;其压力气管也应固定&#xff0c;有条件的可用金属管或塑料管保护。气泡式水位计安装示意图见附图。 2、安装要求 1&#xff09;检查气泡式水位计气管外观有无破损及变形&#xff1b; 2&#xff09;旋开带有…

面试数据库八股文十问十答第十期

面试数据库八股文十问十答第十期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;为什么不推荐多表Join&…

特征工程技巧—Bert

前段时间在参加比赛&#xff0c;发现有一些比赛上公开的代码&#xff0c;其中的数据预处理步骤值得我们参考。 平常我们见到的都是数据预处理&#xff0c;现在我们来讲一下特征工程跟数据预处理的区别。 数据预处理是指对原始数据进行清洗、转换、缩放等操作&#xff0c;以便为…

Blackwell未来发展之路究竟如何?

英伟达Blackwell如何重塑AI计算的未来&#xff1f; 前言 台湾大学演讲 就在6月2日&#xff0c;英伟达CEO黄仁勋在中国台湾大学综合体育馆发表了最新的演讲。这次黄仁勋的演讲依旧重磅&#xff0c;更值得注意的是这次演讲中还透露了Blackwell今后的发展之路。 介绍Blackwell 介绍…