用户反馈任务步骤老是点不到元素,怀疑"执行用的 u2"和"抓取用的 uiautodev"两条 通道不一致。建 research 分支实测,结论与假设相反: 1) **两条通道其实是一致的**:同设备同屏各 dump 一次,节点数 330/330、 id 个数逐项相同 —— 都是同一份 UiAutomation 树,不存在"看到的不一样"。 顺带纠正一处过时注释:uiautodev 的 `rect` 就是像素(`bounds` 才是归一化)。 2) **真凶是序号型选择器**:复现「抖音→我页面」,底部 `0qf` 只有 **3 个** (首页/消息/我 —— 「朋友」tab 是灰度功能,有的账号/设备没有), 而任务里写死 `(…0qf…)[4]` → 第 4 个不存在 → 必然点空。 同一选择器在 4 tab 设备上碰巧对、在 3 tab 设备上必错 —— 这就是"时好时坏"。 3) **解法(实测有效)**:同一 id 多实例时用**文字/描述限定**: `//*[@resource-id="…0qf" and @text="我"]` → 命中并把设备带进我页面(jy- 出现)。 文档里还列了同类限定条件的优先级与四条待排期改动(抓取器优先产语义选择器、 对带序号的选择器加提示、存量任务批量复核、运行期 dump 同 id 实例数)。
4.1 KiB
4.1 KiB
元素选择器研究:为什么"点不到按钮"(2026-09-13)
背景:任务步骤(
click等)频繁出现"元素明明在屏幕上却点不到"。用户怀疑是 任务执行用的 u2 与元素抓取用的 uiautodev 两条通道不一致,要求研究。 分支:research/raw-u2-elements。
一、先否掉一个假设:两条通道并不冲突
| 用的东西 | 连接方式 | |
|---|---|---|
| 任务执行(点击/输入…) | 原生 u2.connect(serial) |
直连设备的 uiautomator2 server |
| 元素抓取(编辑器按钮) | uiautodev 服务(localhost:20242)→ /api/android/{serial}/hierarchy |
它自己的连接 |
实测(同一设备、同一屏、同一时刻,两边各 dump 一次):
| 对比项 | u2 | uiautodev |
|---|---|---|
| 节点总数 | 330 | 330 |
id/0qf(抖音底部 tab) |
3 | 3 |
id/content_layout |
5 | 5 |
逐项一致 —— 两个通道给出的元素树是同一份(都是设备的 UiAutomation dump)。 所以"抓取看到的和执行看到的不是一回事"这个假设不成立。
顺带纠正一处过时注释:
core/uiauto_helper.py说"uiautodev 归一化浮点坐标无法换算像素"。 实际上 uiautodev 的节点里rect就是像素({x,y,width,height}),bounds才是 归一化的 0~1 浮点。要用像素直接用rect即可。
二、真正的原因:序号型选择器((…)[k])会随界面变形而错位
复现(192.168.20.100,抖音 v40.4.0)—— 进「我」页面,底部导航 dump 出来是:
[1] text='首页' bounds=[38,1484][106,1530]
[2] text='消息' bounds=[470,1484][538,1530]
[3] text='我' bounds=[631,1484][665,1530]
只有 3 个 0qf。而任务里的步骤写的是:
(//*[@resource-id="com.ss.android.ugc.aweme:id/0qf"])[4] ← 第 4 个,根本不存在
必然点空 —— 日志里表现为 click 未找到元素,而界面上那个「我」明明就在那儿。
为什么有的设备是 4 个?
抖音底部的「朋友」tab 是灰度功能:有的账号/设备有(首页/朋友/消息/我 = 4 个),
有的没有(首页/消息/我 = 3 个)。同一个序号 [4]:
- 4 个 tab 的设备 → 点到「我」(碰巧对)
- 3 个 tab 的设备 → 点空(或点到别的东西)
同一个选择器在不同机器上语义不同,这是"时好时坏"的根源。
三、解法:用语义选择器,不要依赖序号
实测有效(同一台 3-tab 设备):
# ✗ 序号型:依赖"底部有几个 tab"
(//*[@resource-id="com.ss.android.ugc.aweme:id/0qf"])[4]
# ✓ 语义型:同一个 id,用文字限定 —— 不管有几个 tab 都对
//*[@resource-id="com.ss.android.ugc.aweme:id/0qf" and @text="我"]
验证:命中(bounds 落在「我」上)→ 点击 → 进入我页面 → jy-(切换按钮)出现 ✓
同类可用限定条件(按优先级):
@text="…"—— 文字最稳(tab 名、按钮名)@content-desc="…"—— 无文字但有描述(如首页,按钮)@resource-id="…"—— 唯一时直接用- 组合:
//*[@resource-id="x" and @text="y"]—— 同一 id 多实例时的最佳解 - 结构路径(
…/FrameLayout[2])—— 最后手段,最脆 (…)[k]—— 只在上面都不行时
四、建议的改动(待排期)
- 抓取器(
core/uiauto_helper.py):同一 id 出现多个实例时,优先产出//*[@resource-id="x" and @text="…"]而不是(…)[k];抓取列表里把text/content-desc放在最显眼位置,让用户能看着选。 - 抓取弹窗:对带序号的选择器加醒目提示("这个选择器依赖界面元素个数, 界面一变就失效")。
- 已存任务:把
(…)[k]型选择器扫一遍,能改成文字限定的自动改(脚本), 其余在编辑器里逐个复核。 - 运行期:
click未命中时,日志里顺带 dump 一下"同 id 现在有几个实例", 让"序号错位"当场可诊断(现在是干巴巴一句"未找到元素")。