关于 injecting-input-and-state
面向 Android 测试与 QA 工程师的 ADB Shell 操作技能,介绍如何注入输入、调整屏幕配置和系统设置、清理应用状态及启动 Activity。涵盖现代 `cmd` 命令与旧式做法的差异。
主要功能
- 01通过 input 模拟手势、文字和按键
- 02使用 wm 调整屏幕尺寸与密度
- 03通过 settings 配置测试环境
- 04区分 force-stop 与清除应用数据
- 05使用 am 和 cmd 启动及控制组件
适用场景
- 01在 CI 中脚本化设备上的用户操作
- 02测试不同屏幕尺寸、密度或系统设置
- 03在测试运行之间重置应用状态
- 04启动深层链接并传入 Intent 参数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skydoves/ android-testing-skills --skill injecting-input-and-state安装后在 Claude Code 中输入/injecting-input-and-state确认已加载。
详细介绍
注入输入和状态——从 Shell 控制设备
本技能介绍测试/QA 工程师通过 adb 驱动设备的端侧 Shell 操作:模拟用户手势、覆盖屏幕配置、切换系统设置、重置应用状态,以及启动 Activity。传输方式(adb devices、USB/TCP)请参阅 ../../architecture/understanding-adb-architecture/SKILL.md;驱动设备后采集产物请参阅 ../../capture/capturing-screenshots-and-screenrecord/SKILL.md。
适用场景
- 开发者需要在 CI 中编写用户操作流程脚本,而不编写插桩:点击、滑动、输入文本、按硬件按键。
- 复现 Bug 需要指定屏幕尺寸或密度(
wm size 411x731、wm density 320),且不想重新创建模拟器 AVD。 - 测试套件因动画开启而不稳定;开发者想使用规范的隔离式前置设置(
window_animation_scale = 0等)。 - 开发者需要在每次运行之间清除应用数据,却不确定
am force-stop是否足够(其实不够)。 - 脚本使用
adb shell svc wifi disable,但在 API 30+ 上无声无息地不起作用——解决方法是使用现代命令cmd wifi set-wifi-enabled disabled。 - 开发者需要使用带类型的额外参数(
--ez、--ei、--es)启动深层链接 Activity。
不适用场景
- 需要将字节从设备传出(截图、日志、数据库)。请使用
../../capture/capturing-screenshots-and-screenrecord/SKILL.md、../../observability/extracting-logs-with-logcat/SKILL.md、../../transfer/extracting-test-artifacts/SKILL.md。 - 在插桩测试内部而非主机上驱动设备。请使用
../../../instrumentation/scenarios/launching-activities-with-activityscenario/SKILL.md或 UiAutomator。 - 安装或卸载 APK。请使用
../../apps/installing-and-managing-apps/SKILL.md。 - 首次连接设备。请使用
../../devices/connecting-to-devices/SKILL.md或../../devices/connecting-over-wifi/SKILL.md。
前提条件
- 设备或模拟器可访问(
adb get-state返回device)。 - 主机上安装 Platform Tools 23+。自 Platform Tools 23 起,通过
adb shell传递引号时会经过双层 Shell(类似 ssh)——任何包含空格的字符串都必须注意这一点(developer.android.com/tools/adb#shellcommands)。 - 使用
cmd封装 API 并确保 Shell 退出码可靠:设备 API 24+(cmd在 API 24 引入)。
input — 合成用户事件
input 是设备端对 InputManager.injectInputEvent() 的封装。完整语法:
input [<source>] <command> [<arg>...]
每个命令都有默认的 <source>(tap/swipe 使用触摸屏,text/keyevent 使用键盘)。
点击、滑动、拖放
adb shell input tap <x> <y>
adb shell input swipe <x1> <y1> <x2> <y2> [<duration_ms>]
adb shell input draganddrop <x1> <y1> <x2> <y2> [<duration_ms>]
adb shell input roll <dx> <dy> # rotary/trackball axes
adb shell input motionevent <DOWN|UP|MOVE|CANCEL> <x> <y>
省略 <duration_ms> 时,swipe 默认约为 300 ms。要合成长按,请在原地滑动并持续长按超时时长:input swipe <x> <y> <x> <y> 1000。draganddrop 与 swipe 不同,它会发出可由 View.OnDragListener 处理的拖放手势序列(DOWN → MOVE → UP),而不是与 swipe 相同的事件流。
文本输入——引号陷阱
adb shell input text "<string>"
注意事项(来自 developer.android.com/tools/adb):
- 空格必须编码为
%s。input text "hello world"会输入hello,然后什么也不输入。 - Shell 元字符(
'、"、&、(、)、<、>、|、;、\、反引号)必须转义两次——一次针对本地 Shell,一次针对设备端 Shell。 - 通过
input text输入非 ASCII/Unicode 字符不可靠。请改用 UiAutomator 的UiObject2.setText(...)。
关键事件
adb shell input keyevent [--longpress|--doubletap] <KEYCODE> [<KEYCODE>...]
adb shell input keyevent <NUMERIC_CODE>
与测试相关的 KEYCODE_*(来自 frameworks/base 的 KeyEvent 常量;完整表格见研究笔记 A2):
| 助记名称 | 数值 | 说明 |
|---|---|---|
KEYCODE_HOME | 3 | 按下 Home 键(不一定会唤醒屏幕)。 |
KEYCODE_BACK | 4 | 返回键。 |
KEYCODE_DPAD_UP/DOWN/LEFT/RIGHT/CENTER | 19-23 | 电视 / 焦点遍历。 |
KEYCODE_VOLUME_UP/DOWN/MUTE | 24/25/164 | |
KEYCODE_POWER | 26 | 切换屏幕开 / 关。 |
KEYCODE_TAB | 61 | |
KEYCODE_ENTER | 66 | |
KEYCODE_DEL | 67 | 退格键。 |
KEYCODE_MENU | 82 | |
KEYCODE_APP_SWITCH | 187 | 最近使用的应用按钮。 |
KEYCODE_WAKEUP | 224 | 唤醒但不解锁。 |
KEYCODE_SLEEP | 223 | 强制进入睡眠状态。 |
唤醒 + 解除无锁定模拟器的键盘锁:
adb shell input keyevent KEYCODE_WAKEUP
adb shell input keyevent 82 # menu, dismiss the AOSP swipe-up
输入源前缀
input <source> <command> 映射到 InputDevice.SOURCE_*。当屏幕上有多个输入监听器,或要操作非触摸硬件时,请使用输入源前缀:
| 输入源 | 典型用途 |
|---|---|
touchscreen | tap、swipe、draganddrop 的默认输入源。 |
touchpad | 外接触摸板(Chromebook / Android 平板键盘)。 |
dpad | 电视 / 焦点遍历:input dpad keyevent KEYCODE_DPAD_DOWN。 |
keyboard | text、keyevent 的默认输入源。 |
mouse | 鼠标按键 + 滚动。 |
trackball | 轨迹球滚动。 |
gamepad / joystick | 游戏手柄按键 / 摇杆轴。 |
stylus | 感压触摸(S-Pen)。 |
wm — 覆盖显示尺寸和密度
wm 是 IWindowManager 的 Shell 封装。与测试相关的子命令:
adb shell wm size # read current logical size
adb shell wm size 1080x1920 # override
adb shell wm size reset # restore device default
adb shell wm density # read current dpi
adb shell wm density 320 # override
adb shell wm density reset
adb shell wm dismiss-keyguard # insecure keyguard only
adb shell wm user-rotation [free|lock [<rotation>]] # 0,1,2,3 = 0/90/180/270
使用场景 — 无需重建模拟器即可测试屏幕尺寸 / 密度矩阵:
# Per configuration in the matrix
adb shell wm size 411x731
adb shell wm density 240
# … run tests …
adb shell wm size reset
adb shell wm density reset
注意事项:
- 必须在
@AfterClass/ 套件清理阶段调用reset——否则设备会在运行后保持修改后的配置,影响后续运行和任何人工用户。 wm size/wm density会触发配置变更,并重建前台 Activity。请将它们作为测试间的调整项,而不要在测试过程中使用。am display-size/am display-density是等效的旧式写法;推荐使用wm。
settings — 隔离测试状态
adb shell settings [--user <id>] get <namespace> <key>
adb shell settings [--user <id>] put <namespace> <key> <value> [<tag>] [default]
adb shell settings [--user <id>] delete <namespace> <key>
adb shell settings [--user <id>] reset <namespace> [<package>] [untrusted_defaults|untrusted_clear|trusted_defaults]
adb shell settings [--user <id>] list <namespace>
<namespace> 可以是 global、system、secure 之一。
隔离测试前置设置 — 动画参数
将这三项全部设为 0,以确保插桩测试和屏幕截图测试稳定:
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0
也可以通过 Gradle 设置:testOptions.animationsDisabled = true。
其他与测试相关的键
| 命名空间 | 键 | 用途 |
|---|---|---|
system | font_scale | 浮点数(例如 1.0、1.3);用于字体缩放测试。 |
secure | accessibility_enabled | 设为 1 以启用无障碍功能;配合 enabled_accessibility_services 使用。 |
secure | enabled_accessibility_services | 无障碍测试用的 CSV pkg/.Service。 |
secure | default_input_method | 强制使用已知的 IME。 |
secure | show_ime_with_hard_keyboard | 设为 1,使模拟器上的 IME 保持可见。 |
secure | long_press_timeout | 稳定长按测试。 |
global | airplane_mode_on | 配合 am broadcast android.intent.action.AIRPLANE_MODE 使用。 |
global | policy_control | 例如,使用 immersive.full=* 隐藏系统栏。 |
应用状态 — am force-stop 与 pm clear
| 命令 | 会终止进程? | 会清除数据 / 缓存 / 首选项? | 会重置运行时权限? |
|---|---|---|---|
am force-stop <pkg> | 是 | 否 | 否 |
pm clear <pkg> | 是(隐式) | 是 | 是 |
cmd activity stop-app <pkg> | 是(现代版本,API 28+) | 否 | 否 |
若要在测试之间进行隔离重置,请使用 pm clear。若要“终止进程,使下次启动为冷启动”,使用 am force-stop 即可。有关完整的 pm 命令集,请参阅 ../../apps/installing-and-managing-apps/SKILL.md。
启动 Activity — am start
# Most reliable for tests: explicit component.
adb shell am start -n com.example.app/.MainActivity
# Force-stop first, then launch (clean cold start).
adb shell am start -S -W -n com.example.app/.MainActivity
# Launch by action + URI (default handler for https://).
adb shell am start -a android.intent.action.VIEW -d 'https://example.com'
# Pass typed extras.
adb shell am start -n com.example.app/.MainActivity \
--es screen "settings" --ez debug true --ei count 42
# Return to launcher between tests.
adb shell am start -a android.intent.action.MAIN -c android.intent.category.HOME
Intent 指定符标志(摘自 developer.android.com/tools/adb#IntentSpec):
| Token | 效果 |
|---|---|
-a action | Intent 操作(例如 android.intent.action.VIEW)。 |
-d data_uri | 数据 URI。 |
-t mime_type | MIME 类型。 |
-c category | 类别。 |
-n component | 显式组件(pkg/.Activity)。 |
-f flags | 十六进制 setFlags() 值。 |
| `--es | -e key value` |
--ez key bool | 布尔值 extra。 |
--ei key int | Int extra。 |
--el key long | Long extra。 |
--ef key float | Float extra。 |
--eu key uri | URI extra。 |
--ecn key component | ComponentName extra。 |
--eia/--ela/--efa key v[,v...] | 数组 extra。 |
--activity-clear-top 等 | 映射到 FLAG_ACTIVITY_*。 |
-S | 启动前强制停止目标。 |
-W | 等待启动完成。 |
cmd <service> — 现代封装命令
cmd 是一个通用封装命令,可调用 binder 服务的 onShellCommand。从 API 24 起可用。列出服务:adb shell cmd -l(或 dumpsys -l)。与测试相关的入口:
| 调用方式 | 效果 |
|---|---|
cmd activity stop-app <pkg> | am force-stop 的现代等效命令。 |
cmd activity broadcast … | 与 am broadcast 相同。 |
cmd package install -r -t -g <path> | 等效于 pm install。 |
cmd package compile -m speed -f <pkg> | 强制进行 AOT 编译。 |
| `cmd appops set allow | deny |
cmd jobscheduler run -f <pkg> <jobId> | 立即强制运行 JobService。 |
| `cmd uimode night yes | no |
cmd locale set-app-locales <pkg> --locales en-US | 设置每个应用的语言区域(Android 13+)。 |
cmd statusbar expand-notifications / collapse | 展开/收起通知栏。 |
| `cmd wifi set-wifi-enabled enabled | disabled` |
| `cmd connectivity airplane-mode enable | disable` |
cmd testharness enable | 将设备重置为隔离的测试框架状态(Android 10+)。 |
API 30+ 上 svc 已弃用
除非从特权 Shell 调用,否则 API 30+ 上的 svc data、svc wifi、svc bluetooth 不会执行任何操作(corpus §I.10)。推荐使用上文的 cmd 封装命令替代。
模式
模式:错误 — 在 API 30+ 上使用 svc wifi disable
# WRONG
adb shell svc wifi disable
# WRONG because: silently no-ops on API 30+ since the svc CLI was demoted from a privileged
# helper to an ordinary shell command. Exit code 0, Wi-Fi still on. The script proceeds
# under a wrong assumption — exactly the worst kind of failure mode.
# RIGHT
adb shell cmd wifi set-wifi-enabled disabled
adb shell cmd wifi status # verify
模式:错误 — 使用 am force-stop 在测试之间重置
# WRONG
adb shell am force-stop com.example.app
./run-next-test.sh
# WRONG because: am force-stop kills the process but leaves shared prefs, databases, the
# image cache, and granted runtime permissions intact. The "next test" inherits state
# from the previous run.
# RIGHT
adb shell pm clear com.example.app
adb shell pm clear com.example.app.test # also reset test process state
./run-next-test.sh
(在 Gradle 中,Test Orchestrator 配合 clearPackageData: 'true' 可实现等效效果 — 请参阅 ../../tests/running-instrumented-tests-via-adb/SKILL.md。)
模式:错误 — input text "hello world"
# WRONG
adb shell input text "hello world"
# WRONG because: spaces in `input text` MUST be encoded as %s. The above types only "hello".
# RIGHT
adb shell input text 'hello%sworld'
模式:隔离环境预备步骤 + Activity 启动
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0
adb shell pm clear com.example.app
adb shell am start -S -W -n com.example.app/.MainActivity
-S 会先强制停止;-W 会阻塞,直到启动完成。
模式:通过方向键进行电视 / 焦点导航
adb shell input dpad keyevent KEYCODE_DPAD_DOWN
adb shell input dpad keyevent KEYCODE_DPAD_DOWN
adb shell input dpad keyevent KEYCODE_DPAD_CENTER
为事件添加 dpad 源前缀,可确保事件路由到电视风格的焦点处理器,而不是触摸屏事件处理流程。
强制规则
- 必须在 API 30 及更高版本上使用
cmd wifi set-wifi-enabled/cmd connectivity airplane-mode/cmd bluetooth_manager enable。不得依赖svc wifi/svc bluetooth/svc data——这些命令在现代版本上会静默地不执行任何操作。 - 必须在目标是实现测试之间的隔离重置时使用
pm clear <pkg>(而非am force-stop)。force-stop只会终止进程;不会清除数据。 - 必须将
input text中的空格编码为%s;必须对包含 shell 元字符的字符串使用双层 shell 引号(ssh 风格)。Platform Tools 23+(developer.android.com/tools/adb#shellcommands)。 - 必须在套件拆解时调用
wm size reset和wm density reset,前提是套件曾覆盖这些值。未清理的覆盖会影响该设备上的所有后续运行。 - 必须在任何断言 UI 的测试运行前,将三个动画缩放比例设为
0(或在 Gradle 中使用testOptions.animationsDisabled = true)。 - 不得在
adb shell input调用之间使用Thread.sleep来“等待 UI 跟上”。在插桩测试中,使用 Compose 的空闲同步(../../../compose/synchronization/synchronizing-with-idle/SKILL.md);在主机脚本中,使用adb shell am start -W阻塞等待 Activity 启动,并在重启之间使用adb shell wait-for-device。 - 不得假设
adb shell am instrument在不带-w时会返回有意义的退出码。参见../../tests/running-instrumented-tests-via-adb/SKILL.md和../../automation/scripting-adb-for-ci/SKILL.md。 - **首选:**在测试中使用
am start -S -W -n pkg/.Activity启动冷启动应用——-S会重置,-W会阻塞直到就绪。
验证
- 测试运行前,
adb shell settings get global window_animation_scale返回0(或0.0);transition_animation_scale和animator_duration_scale也应如此。 -
scripts/或.github/workflows/中没有脚本使用svc wifi/svc bluetooth/svc data。 - 每个测试之间的
am force-stop都与pm clear配套使用(或由其替代)。 - 不存在
input text "<string with spaces>";所有空格都使用%s。 - 设置中的每个
wm size <override>都在拆解中有对应的wm size reset。 - CI 连续通过 50 次运行,且未出现“设备进入先前配置”的偶发失败。
参考资料
- developer.android.com/tools/adb —
am、pm、input、wm、settings、intent 规范(https://developer.android.com/tools/adb)。 - developer.android.com/tools/adb#shellcommands — Platform Tools 23 起通过
adb shell进行引号处理。 - developer.android.com/reference/android/view/KeyEvent —
KEYCODE_*常量表。 - developer.android.com/training/testing/instrumented-tests/stability — 动画缩放设为零的标准方法。
- developer.android.com/studio/test/advanced-test-setup#use-gradle-managed-devices —
testOptions.animationsDisabled。 - 研究笔记
tasks/research/A2-adb-shell-commands.md— 完整的input/wm/settings/am/pm/cmd/svc表格和逐字引文。 - 同级技能:
../../architecture/understanding-adb-architecture/SKILL.md— 三段式架构、服务端、守护进程。 - 同级技能:
../../devices/connecting-to-devices/SKILL.md—adb devices状态、wait-for-device。 - 同级技能:
../../devices/connecting-over-wifi/SKILL.md—adb pair/adb connect。 - 同级技能:
../../apps/installing-and-managing-apps/SKILL.md— 完整的pm install/pm uninstall/pm grant功能。 - 同级技能:
../../tests/running-instrumented-tests-via-adb/SKILL.md—am instrument -w -r。 - 同级技能:
../../capture/capturing-screenshots-and-screenrecord/SKILL.md— 操作设备后捕获产物。 - 同级技能:
../../observability/extracting-logs-with-logcat/SKILL.md— 读取设备日志。 - 同级技能:
../../transfer/extracting-test-artifacts/SKILL.md—adb pull/adb push/run-as。 - 同级技能:
../../automation/scripting-adb-for-ci/SKILL.md— bash 习惯用法、重试、端口转发。 - 跨类别:
../../../instrumentation/scenarios/launching-activities-with-activityscenario/SKILL.md— 在插桩测试内部启动。 - 跨类别:
../../../fundamentals/strategies/applying-testing-strategies/SKILL.md— 何时通过adb而非插桩来操作。