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 下的所有根节点信息,完成了控件遍历起点的准备工作,接下来就是遍历根节点的子节点,完成控件搜索的过程,参考这篇文章:
这里,要提几点的是:
- 获取的所有 Window 是包含 Active Window 的,所以其根节点也会被重复读取一次
- 为何不直接通过该方法获取,而需要专门先获取一次呢?因为这样就能达到优先搜索 Active Window 下控件的目的,减少搜索的时间
- 有时 getRootInActiveWindow 返回 null,但仍然可以通过 Window 来获取 root node
通过 Window root 来遍历控件是核心入口,通过它逐步开枝散叶去查找所有控件 herarchy,而作为二次开发的核心,也可以从 AccessibilityNodeInfo 的产生入手去探索研究