@@ -6,12 +6,12 @@
## 环境
```bash
~/Library/Android/sdk /emulator/emulator -list-avds # Medium_Phone_API_36.1(手机) / Aerol ogic_Tablet(平板)
/opt/homebrew/share/android-commandlinetools /emulator/emulator -list-avds # 本机现仅 AeroL ogic_Phone( 手机)
adb devices -l
adb -s <serial> shell wm size && adb -s <serial> shell wm density
```
判定形态:手机 1080× 2 400 @420 dpi → `PHONE(sw=411 dp)` ;平板 1280× 800@160dpi → `TABLET(sw=800dp)` 。
判定形态:手机 720× 14 40@292 dpi → `PHONE(sw≈394 dp)` ;平板 1280× 800@160dpi → `TABLET(sw=800dp)` (本机平板 AVD 待建,见文末工具选型纪要) 。
`ui_probe.sh` 封装了常用操作,比手写 adb 省事且更可靠:
@@ -46,7 +46,7 @@ adb -s <serial> install -r -d app/build/outputs/apk/debug/app-debug.apk
adb -s <serial> shell monkey -p com.lukouguoji.aerologic -c android.intent.category.LAUNCHER 1
```
测试环境账号在记忆文件 `test-env-login.md` ( ADMIN / 用户已授权保存 ),可自主登录。
测试环境账号在记忆文件 `aerologic-test-account.md` ( 本机, admin / 用户已授权保存;旧机器文件名为 ` test-env-login.md` ),可自主登录。
填表用 `input text` , **`#` 不需要转义**——新版 adb 的 input text 参数不经远端 shell 解析,
写 `\#` 反而会把 `\` 也输进去(实测多出一位)。
@@ -179,3 +179,88 @@ adb -s <serial> logcat -d -s DBG | grep -A14 "'旧值' -> ''" | grep "at com.luk
本次靠这招定位到 `TextView.onRestoreInstanceState → EditText.setText → doOnTextChanged` ,
即复合自定义 View 的 id 串档(见 `components.md` )。**排完删日志。**
---
## 工具选型纪要: ADB(ui_probe.sh) × Mobile MCP 分工( 2026-10-02)
背景:`mobile-mcp` ( `@mobilenext/mobile-mcp` )已装入项目级 `.mcp.json` ,与本手册的
ADB/ui_probe.sh 流程并存。以下基于 2026-10-02 在 `AeroLogic_Phone` 模拟器上的实测对比
(登录流程实测到“填表”一步,因内网不通放弃后续),给出后期开发的工具选择规则。
### 能力对照(只列有差异的维度)
| 维度 | ADB + ui_probe.sh | Mobile MCP |
|------|-------------------|------------|
| 元素定位 | `tap-text` 按文字点,**每次点击前重新 dump**,天然抗布局变化;同名多处可指定序号 | 按 ref 点,键盘弹出/页面导航后 **ref 失效** ,必须重新 list( 实测踩过) |
| 元素读取 | `texts` 只出可见文字, token 便宜,配 grep 断言(`texts \| grep -c "全选"` ) | 完整控件树( text/id/类型/focused/disabled/checked/坐标),信息全但 **token 重** (登录页 54 行) |
| 文本输入 | `input text` + `keycombination` ( CTRL+A/DEL 清空——登录 bcrypt 回填坑的标准解法) | `type_keys` 只能输入,**无清空原语**(实测需回落 adb) |
| 请求核对 | `req <关键字>` ( OkHttp 多行 pretty JSON 按关键字截取,为本项目定制) | `get_device_logs` 可过滤 tag/进程,多行报文截取不如 `req` 顺手 |
| 崩溃检查 | `crash` | `list_crashes` / `get_crash` (等价) |
| 前台断言 | `dumpsys activity` (手写 grep) | `get_foreground_app` 一次调用 |
| 录屏 | `adb shell screenrecord` (手拉文件) | `start/stop_screen_recording` 直接回传路径,更省事 |
| 批量操作 | 脚本串联,每条一次往返 | `mobile_batch_commands` 一次往返多步(**页面稳定时**才高效) |
| iOS / 云真机 | 无 | 有(本项目 Android-only, 暂无用武之地) |
| 依赖 | 零依赖(本机 adb 即可) | npx 每会话冷启动、Node 20+、`@latest` 有版本漂移风险(建议固定 `@1.0.7` ) |
### 选型规则(后期开发按此执行)
**默认走 ADB + ui_probe.sh** 。本手册全部流程不变:构建安装、冷启动/直启、登录、
wait-text 就绪等待、tap-text 交互、texts 断言、req 抓包、crash 归零、方向采样、Pad 回归、
样例 Bean 注入。理由: 为本项目的坑定制( bcrypt 回填、Loading 吞点击、参数名 A/B) 、
token 成本低、零外部依赖,且 tap-text 每次重新 dump, 比 ref 更抗布局变化。
**以下四种场景切 Mobile MCP** :
1. **控件级语义断言** ——要看 disabled/checked/focused 状态、控件 id、输入框变换后的值
(如“密码框 9 个点”“角色 Spinner 是否禁用”“用户名框自动大写成 ADMIN”) 。元素树比
截图肉眼判读可靠,比 `texts` 信息全。实测:登录页一次拿到 `inputPass text="•••••••••"` 、
角色 Spinner `disabled` ,这类断言 `texts` 做不到(密码框无明文文字)
2. **页面稳定时的多步连续输入** ——list 一次拿 ref, `batch_commands` 一次往返完成
tap→type→tap→type; 比逐条 adb 省 round-trip。前提: 中间不弹键盘收不起/不导航
(弹了就见下方回落规则)
3. **录屏取证** ——复现“先横屏闪 1 秒”类**时序 bug**, mp4 归档进问题清单,比连拍截图省力
4. **前台应用/安装卸载等状态核查** ——`get_foreground_app` 、`install/uninstall/launch_app`
一步到位,省得记 dumpsys/am 语法
**回落规则(两者随时可混用,操作同一设备无状态冲突)** :
- mobile-mcp ref 失效(键盘弹出/导航后点击落空)→ 重新 `list_elements` ,或直接换
`ui_probe.sh tap-text` (按文字,永远新鲜)
- 需要清空输入框(登录 bcrypt 回填)→ 只能 adb: `input keycombination 113 29` + DEL
- 多行请求/响应体核对 → `req` ,不用 mobile-mcp 的日志过滤硬凑
### 实测踩坑( Mobile MCP, 2026-10-02)
- ⚠️ **键盘弹出会平移布局** :之前 list 出的 ref/坐标失效。batch 里连续 click+type 会把
两段文本打进同一个框(用户名框变成 `ADMINAA123456#` )。规避:每步输入完先
`KEYCODE_BACK` 收键盘再点下一个目标;或点完先确认焦点再 type
- 无“清空字段”原语;`type_keys` 输入即所得,不会帮你选中文本
- 元素树 token 开销大,断言优先 `texts | grep` ,确实要看状态/id 才上 list_elements
### 本机环境更新( 2026-10-02)
- SDK 根目录:`/opt/homebrew/share/android-commandlinetools` ( brew 安装, emulator 包已装)
- 手机 AVD: `AeroLogic_Phone` ——720× 1440、5.51 寸 292dpi( sw≈394dp, 落手机桶 360~411dp) 、
Android 12 / API 31( 与 targetSdk 一致) 、arm64 纯 64 位(本项目无 abiFilters, 唯一含
.so 的 fimage.aar 自带 arm64-v8a, 兼容) 、无边框( `showDeviceFrame=no` )
- ⚠️ **旧文档里的 `Aerologic_Phone_5.5_API31` ( 300dpi/sw=384dp) 、`Medium_Phone_API_36.1` 、
`Aerologic_Tablet` 均为旧机器 AVD, 本机不存在**。平板回归前需先建平板 AVD
( 1280× 800@160dpi , `avdmanager create avd` 后照本文「Pad 端回归」执行)
- 启动:`/opt/homebrew/share/android-commandlinetools/emulator/emulator -avd AeroLogic_Phone`
( Quick Boot 快照已存在,二次启动数秒就绪)
- 模拟器 NAT 出网,**内网服务不可达**(本次登录实测“服务器不通”而放弃)——需要真实接口
联调时先确认宿主机网络可达内网;不可达时按上文「无数据时怎么核对 UI」注入样例 Bean
- `.mcp.json` 已加 `mobile-mcp` 条目( npx stdio) , **建议把 `@latest` 固定为 `@1.0.7` **;
该文件 git 跟踪,改动需提交后队友才可用
### 推荐配合模式(单页验证的完整动线)
adb 骨架 + mobile-mcp 点睛,一条龙:
1. **adb** :构建安装 → 冷启动/直启(`am start` )→ 按登录节填表(含 bcrypt 清空顺序)
2. **ui_probe** : `wait-text` 就绪 → `tap-text` 交互 → `texts | grep` 断言 → `req` 抓包 → `crash` 归零
3. `texts` 断言不够(要看状态/id/变换值)或要连续多步输入 → **mobile-mcp** :
`list_elements` → `batch_commands` ( tap/type)
4. 时序类 bug( 闪屏/动画/异步错位)→ **mobile-mcp 录屏**取证,问题清单贴 mp4 + 截图
5. 全程崩溃检查两家等价(`crash` / `list_crashes` ),任选