UIAutomator 获取控件信息

Feb 17 2020

Android UIAutomator 是调用的 Accessibility 中的逻辑来实现对 UI 控件的查找。今天分享一下关于 UI 控件获取的逻辑

UiDevice 中有一个方法是用来 dump 控件结构树的,如下

1
2
3
4
5
6
public static void dumpWindowHierarchy(UiDevice device, OutputStream out) throws IOException {
......
for (AccessibilityNodeInfo root : device.getWindowRoots()) {
dumpNodeRec(root, serializer, 0, device.getDisplayWidth(), device.getDisplayHeight());
}
......

其中有一个核心方法 getWindowRoots,是今天的主角。该方法通过一个集合来存储 AccessibilityNodeInfo,表明不会存放重复的 Node,同时,这个类可以简单理解为 View 的快照,View 的状态可以从该类属性中读取到

首先尝试获取一个 Active Window 的根 root

1
AccessibilityNodeInfo activeRoot = getUiAutomation().getRootInActiveWindow();

Active Window 是这样的 Window: 当前用户正在触摸(touching) 的窗口或者具备输入焦点(input focus)的窗口(如果用户当前没有touch 窗口),如果这个根 root 非 null 则放入 Set 中作为第一个元素

1
Set<AccessibilityNodeInfo> roots = new HashSet();

接下来,尝试获取当前 Screen 的所有 Window 的快照 AccessibilityWindowInfo,不过这必须是 API 21 及以上版本才支持,现在的机型都可以认为已经支持了

1
List<AccessibilityWindowInfo> windows = getUiAutomation().getWindows();

并且获取到每个 Window 的根节点快照,如果非 null 就添加到 Set 中

1
2
3
4
5
6
7
for (AccessibilityWindowInfo window : windows) {
AccessibilityNodeInfo root = window.getRoot();
if (root == null) {
continue;
}
roots.add(root);
}

至此,就获取到了当前 Screen 下的所有根节点信息,完成了控件遍历起点的准备工作,接下来就是遍历根节点的子节点,完成控件搜索的过程,参考这篇文章:

这里,要提几点的是:

  1. 获取的所有 Window 是包含 Active Window 的,所以其根节点也会被重复读取一次
  2. 为何不直接通过该方法获取,而需要专门先获取一次呢?因为这样就能达到优先搜索 Active Window 下控件的目的,减少搜索的时间
  3. 有时 getRootInActiveWindow 返回 null,但仍然可以通过 Window 来获取 root node

通过 Window root 来遍历控件是核心入口,通过它逐步开枝散叶去查找所有控件 herarchy,而作为二次开发的核心,也可以从 AccessibilityNodeInfo 的产生入手去探索研究