4、UI Automator 框架入门:使用 UI Automator 进行基本元素定位
UI Automator 是 Android 官方提供的跨应用 UI 自动化测试框架。说白了,它能让你的代码像真实用户一样操作手机——启动 App、点击按钮、滑动列表。特别在做 TalkBack 无障碍测试时,UI Automator 几乎是绕不开的工具。
我个人习惯先把框架的定位原理弄明白,再动手写代码。嗯,先看下面这张图,把整体脉络理清楚:
4.1 核心对象:UiSelector
UI Automator 的一切定位都围绕着 UiSelector。你可以把它想象成一个「筛子」——指定条件,框架就从当前界面里把符合条件的控件筛出来。最常用的方法有这些:
| 定位方式 | UiSelector 方法 | 适用场景 | 稳定度 |
|---|---|---|---|
| 资源 ID | .resourceId(String) | 有唯一 ID 的控件 | ★★★★★ |
| 文本内容 | .text(String) | 按钮、标签文本固定 | ★★★☆☆ |
| 内容描述 | .contentDescription(String) | TalkBack 无障碍节点 | ★★★★☆ |
| 类名 | .className(String) | 一类控件批量操作 | ★★☆☆☆ |
| XPath(通过 UiDevice.findObject() + By.clazz 等) | .fromParent() / .childSelector() | 复杂层次定位 | ★★★☆☆ |
关键技巧:对于 TalkBack 自动化测试,contentDescription 是最可靠的定位属性。因为 TalkBack 朗读用的就是这个字段,你定位的元素就是用户听到的元素。我曾经在测试一个新闻 App 时,用 text 死活定位不到「分享」按钮,后来发现开发者设置了 contentDescription="分享文章",改用 contentDescription 后一次通过。
4.2 基本定位与操作
先看一个最常用的例子:在系统设置里点击「无线和网络」。注意这段代码演示了如何等待元素出现、定位并点击。
// 获取 UiDevice 实例
UiDevice device = UiDevice.getInstance(instrumentation);
// 等待系统设置界面加载完成
device.waitForIdle(3000);
// 通过内容描述定位(TalkBack 友好)
UiObject2 networkSetting = device.findObject(
By.desc("无线和网络")
);
if (networkSetting != null) {
networkSetting.click();
System.out.println("定位成功并点击了:「无线和网络」");
} else {
System.out.println("未找到元素,请检查 contentDescription 是否设置");
}
这里用了 By.desc(),它是 contentDescription 的快捷写法,等价于 By.clazz(UiSelector().contentDescription(...))。我个人建议在无障碍测试中优先使用 By.desc(),因为 TalkBack 焦点就是根据这个属性移动的。
4.3 进阶定位:链式筛选与父节点
现实情况往往更复杂——同一个界面可能有多个相同类名的控件。比如一个列表里有十几个「TextView」,光用 className 肯定不行。这时可以用链式筛选:
// 找到父容器(比如 RecyclerView 的 contentDescription)
UiObject2 parent = device.findObject(By.desc("消息列表"));
// 在父容器内定位第一个文本为"你好"的控件
UiObject2 target = parent.findObject(By.text("你好"));
if (target != null) {
target.click();
}
你想想看,这种「先找容器再找元素」的方式,是不是很像你在真实操作时先看区域再点按钮?UI Automator 的 childSelector 也是同样的思路。我曾经踩过一个坑:直接用 device.findObject(By.text("确定")) 的时候,界面上有两个「确定」按钮——一个在底部弹窗里,一个在顶部导航栏。结果每次点击的都是顶部的那个,导致弹窗永远关不掉。后来改用父容器限定才解决。
注意:不要滥用 XPath 或通过 By.xpath() 定位。UI Automator 的 XPath 支持有限且性能很差。在 TalkBack 测试中尤其要避免,因为无障碍节点树可能比布局树更复杂,XPath 解析容易超时。我建议用 By.clazz() + By.depth() 或者多级 findObject 替代。
4.4 定位失败时的排查方法
刚开始学 UI Automator 时,最崩溃的就是「明明元素在屏幕上,却定位不到」。别急,我总结了几个排查步骤:
- 确认当前界面:用
device.getCurrentPackageName()检查是不是在你想要的 App 里。 - 打印布局树:调用
device.dumpWindowHierarchy(sdcard/tmp.xml)把当前页面 dump 出来,然后在文件中搜索你的目标文本或 contentDescription。 - 检查属性是否为空:很多自定义 View 没有设置 contentDescription,导致
By.desc()永远找不到。 - 等待时间不够:网络加载、动画过渡都会让元素延迟出现。用
device.wait(Until.findObject(By.desc("xxx")), 5000)代替直接 find。
小技巧:在 Android Studio 的 Device Explorer 里,可以直接导出 dump.xml 用浏览器打开查看树结构,比在控制台看纯文本方便多了。
4.5 实战:定位 TalkBack 首焦元素
TalkBack 开启后,屏幕上会有一个醒目的「绿色框」——那就是当前无障碍焦点。在自动化测试中,我们有时需要拿到这个焦点对应的元素。可以这样做:
// 获取当前具有无障碍焦点的元素
UiObject2 focused = device.findObject(By.focused(true));
if (focused != null) {
String desc = focused.getContentDescription();
String text = focused.getText();
System.out.println("当前焦点元素 contentDescription: " + desc + ", text: " + text);
}
这里用 By.focused(true) 快速找到 TalkBack 正在朗读的那个控件。如果你需要在测试中模拟 TalkBack 的下一步操作(比如下滑切换焦点),可以用 device.pressDPadDown() 或 device.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)。不过这些属于更高级的技巧,后续章节会详细展开。
记住:UI Automator 的定位能力非常强大,但用得巧不巧,决定了你测试脚本的稳定性。多用 contentDescription,善用层级过滤,别偷懒用 XPath,你的测试就能跑得更稳。