From a94f63f0bf26c49d0b81caf02d5911070861c4a3 Mon Sep 17 00:00:00 2001 From: butubb <1422726308@qq.com> Date: Sun, 13 Sep 2026 20:56:37 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=85=83=E7=B4=A0=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=99=A8=E7=A0=94=E7=A9=B6=E2=80=94=E2=80=94=E3=80=8C=E7=82=B9?= =?UTF-8?q?=E4=B8=8D=E5=88=B0=E6=8C=89=E9=92=AE=E3=80=8D=E7=9A=84=E6=A0=B9?= =?UTF-8?q?=E5=9B=A0=E6=98=AF=E5=BA=8F=E5=8F=B7=E5=9E=8B=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=99=A8=EF=BC=88=E9=99=84=E5=AE=9E=E6=B5=8B=E8=AF=81=E6=8D=AE?= =?UTF-8?q?=E4=B8=8E=E8=A7=A3=E6=B3=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户反馈任务步骤老是点不到元素,怀疑"执行用的 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 实例数)。 --- doc/README.md | 1 + doc/research/U2_ELEMENT_SELECTORS.md | 98 ++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 doc/research/U2_ELEMENT_SELECTORS.md diff --git a/doc/README.md b/doc/README.md index 10e742a..de7e852 100644 --- a/doc/README.md +++ b/doc/README.md @@ -24,6 +24,7 @@ | [STF_REMOVAL.md](STF_REMOVAL.md) | **历史记录**:摘除 OpenSTF 的迁移过程(阶段 0-3) | 追溯背景时参考 | | [staffdeck/KNOWLEDGE_BASE.md](staffdeck/KNOWLEDGE_BASE.md) | 给 StaffDeck 数字员工的知识库(MCP 接入/工具/约定/红线) | 外部 AI 接入方 | | [staffdeck/JOB_SPEC.md](staffdeck/JOB_SPEC.md) | 数字员工岗位说明(岗位描述/看板摘要/执行约束) | 外部 AI 接入方 | +| [research/U2_ELEMENT_SELECTORS.md](research/U2_ELEMENT_SELECTORS.md) | **元素选择器研究**:「点不到按钮」的根因(序号型选择器随界面变形而错位)与语义选择器解法 | 写任务/抓元素的开发 | | [backlog/TODO.md](backlog/TODO.md) | 已确认但暂缓的待办(含已知问题) | 所有开发者 | 项目根目录的 [README.md](../README.md) 是**项目总览与快速上手**(面向第一次接触项目的人),细节都在本目录。 diff --git a/doc/research/U2_ELEMENT_SELECTORS.md b/doc/research/U2_ELEMENT_SELECTORS.md new file mode 100644 index 0000000..3cbf648 --- /dev/null +++ b/doc/research/U2_ELEMENT_SELECTORS.md @@ -0,0 +1,98 @@ +# 元素选择器研究:为什么"点不到按钮"(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 设备): + +```python +# ✗ 序号型:依赖"底部有几个 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-`(切换按钮)出现 ✓ + +同类可用限定条件(按优先级): + +1. `@text="…"` —— 文字最稳(tab 名、按钮名) +2. `@content-desc="…"` —— 无文字但有描述(如 `首页,按钮`) +3. `@resource-id="…"` —— **唯一**时直接用 +4. 组合:`//*[@resource-id="x" and @text="y"]` —— **同一 id 多实例时的最佳解** +5. 结构路径(`…/FrameLayout[2]`)—— 最后手段,最脆 +6. `(…)[k]` —— **只在上面都不行时** + +--- + +## 四、建议的改动(待排期) + +1. **抓取器(`core/uiauto_helper.py`)**:同一 id 出现多个实例时,**优先产出 + `//*[@resource-id="x" and @text="…"]`** 而不是 `(…)[k]`;抓取列表里把 + `text`/`content-desc` 放在最显眼位置,让用户能看着选。 +2. **抓取弹窗**:对**带序号**的选择器加醒目提示("这个选择器依赖界面元素个数, + 界面一变就失效")。 +3. **已存任务**:把 `(…)[k]` 型选择器扫一遍,能改成文字限定的自动改(脚本), + 其余在编辑器里逐个复核。 +4. **运行期**:`click` 未命中时,日志里顺带 dump 一下"同 id 现在有几个实例", + 让"序号错位"当场可诊断(现在是干巴巴一句"未找到元素")。