先看一个 demo,这是给 Agent 用的杀手级动作 —— hs ui -i 把整棵 UI 树压成一张可读的扁平表,只留下能点 / 能填 / 能看的节点:
$ hs ui -i
@(54,160) click ImageButton desc="返回"
@(540,360) TextView #title "登录你的账户"
@(540,540) click,focus EditText #email ""
@(540,640) click,password EditText #password ""
@(540,760) check CheckBox "记住我"
@(540,860) click Button #continue "继续"
@(540,960) click TextView "忘记密码?"
每行四列:中心坐标 / 行为标签 / 类名+id / 文本或 desc。完美的 LLM-friendly 表达 —— 喂给模型就能让它做决策,比丢一整棵 XML 节省 10-100× 的 token,定位也直接给出可点的中心点。
把它跟 hs tap 串起来,一行就能完成 "找 Login 按钮、点它":
hs tap "继续" # 文本匹配
# 或者更稳的 CSS-like 选择器
hs find 'Button[text="继续"]' | head -1 # 拿坐标
hs type EditText "user@example.com" # ACTION_SET_TEXT,绕开 IME
hs wait com.foo/.HomeActivity # 事件驱动等待,不轮询
写过 Android 自动化、爬手机、UI 测试、Agent 控机的应该都被 adb 折磨过:每条命令都要 fork → shell → app_process 冷启动一遍,跑 N 条命令就交 N
次启动税。dumpsys、getprop、settings 这种读得最勤的状态从来没人帮你缓存。UI 操作没有 CSS 选择器、没有原子 set text 、没有 event-driven 等待。
Handsets 把这块重写了:
- 设备端是一个常驻 JVM daemon(
app_process跑,shell UID,hidden-API 已解锁),用一条 TCP 长链跟主机通信。 - 主机端 CLI
hs是 Rust pure std,零第三方依赖,单文件 1MB 出头。 - 主机后台 push 一份 state 镜像到
~/.handsets/state-<port>.json,hs info/hs show直接读本地文件,亚毫秒级。
为什么有必要(vs adb,同机模拟器实测)
命令 hs adb 提速hs state X(host 缓存读)
0.21 µs
100+ ms 走 dumpsys
~10 000×
hs see x.jpg(截图)
7.7 ms
705 ms
92×
hs info(12 字段快照)
2.5 ms
200+ ms 串多次 getprop
80×+
hs show top
2.0 ms
86 ms
43×
hs prop KEY
1.6 ms
46 ms
29×
hs settings get
4.5 ms
69 ms
15×
跑 100 条 dump_active 整体 0.91s vs adb 的 1.65s,单次差距越大、批量收益越大。Agent 这种高频小命令场景一上量特别明显。
跟 uiautomator2 / Appium 比有什么不一样
vs uiautomator2(openatx)
- 架构:uiautomator2 = atx-agent +
com.github.uiautomator两个 apk + HTTP/JSON,每次操作进 UIAutomator instrumentation 框架,光 framework overhead 就吃几十 ms 。Handsets 直接app_process跑轻量 daemon,绕开 UIAutomator,直接打 binder/反射调系统服务。 - 协议:HTTP/JSON vs TCP 长链 + 二进制帧,单次操作没有 HTTP/JSON 开销。
- 状态:
d.info/d.app_current()每次都 round-trip;Handsets 推到本地文件,file read,0.21µs。 - 安装:uiautomator2 要装 apk(PackageInstaller 、签名、弹窗);Handsets 只是
adb push hs.jar,不装 apk。 - UI 表达:
d.dump_hierarchy()返回完整 XML(几百 KB);hs ui -i直接给一张扁平可读表,Agent / LLM 场景下 token 用量差一个数量级。
vs Appium
- 重量:Appium = Node server + appium-uiautomator2-driver apk + WebDriver 链,启动几秒。
hs use< 200ms 。 - 协议:WebDriver(HTTP)vs 原生 TCP,没有 W3C 那套握手开销。
- 场景:Appium 是 cross-platform CI 测试最优解(iOS + Android 、selenium-like API 、录制回放),Handsets 偏 agent / 自动化 / 命令行驱动 / 高频小命令 —— 不需要 WebDriver 协议时,这层全是负担。
- 选择器:
AndroidUIAutomator/AccessibilityIdvs CSS-like:hs find 'TextView[text~=Login], Button[desc=Sign in]',逗号 = OR,短而熟悉。
老实说哪边不如它们:uiautomator2 / Appium 有录制工具、IDE 集成、test runner 、报告框架。Handsets 是 lean CLI,目前没有生态层的东西。写 pytest 跑回归测试出 HTML 报告,还是用 uiautomator2 + pytest 更顺手。Handsets 适合的是 「 LLM agent / 脚本 / 命令行循环」 这种你只关心单次延迟和组合性的场景。
安装(macOS / Linux 都有 release 包)
curl -fsSL https://raw.githubusercontent.com/elliotgao2/handsets/main/install.sh | bash
hs use
hs ui -i # 试试这个,见上面
CI 会在 tag 上自动 cross-build macOS arm64/x86_64 、Linux x86_64/aarch64,附 SHA256 。
GitHub: https://github.com/elliotgao2/handsets
欢迎拍砖,特别是用过 uiautomator2 / Appium 的同学,或者正在做 LLM 控机 / Agent 自动化的同学,看看哪些场景值得再优化。