mcpField
I
injecting-input-and-state

injecting-input-and-state

github.com/skydoves测试与质量33972 次安装22 周前更新
代码仓库

关于 injecting-input-and-state

面向 Android 测试与 QA 工程师的 ADB Shell 操作技能,介绍如何注入输入、调整屏幕配置和系统设置、清理应用状态及启动 Activity。涵盖现代 `cmd` 命令与旧式做法的差异。

主要功能

  1. 01通过 input 模拟手势、文字和按键
  2. 02使用 wm 调整屏幕尺寸与密度
  3. 03通过 settings 配置测试环境
  4. 04区分 force-stop 与清除应用数据
  5. 05使用 am 和 cmd 启动及控制组件

适用场景

  1. 01在 CI 中脚本化设备上的用户操作
  2. 02测试不同屏幕尺寸、密度或系统设置
  3. 03在测试运行之间重置应用状态
  4. 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_HOME3按下 Home 键(不一定会唤醒屏幕)。
KEYCODE_BACK4返回键。
KEYCODE_DPAD_UP/DOWN/LEFT/RIGHT/CENTER19-23电视 / 焦点遍历。
KEYCODE_VOLUME_UP/DOWN/MUTE24/25/164
KEYCODE_POWER26切换屏幕开 / 关。
KEYCODE_TAB61
KEYCODE_ENTER66
KEYCODE_DEL67退格键。
KEYCODE_MENU82
KEYCODE_APP_SWITCH187最近使用的应用按钮。
KEYCODE_WAKEUP224唤醒但不解锁。
KEYCODE_SLEEP223强制进入睡眠状态。

唤醒 + 解除无锁定模拟器的键盘锁:

adb shell input keyevent KEYCODE_WAKEUP
adb shell input keyevent 82                     # menu, dismiss the AOSP swipe-up
输入源前缀

input <source> <command> 映射到 InputDevice.SOURCE_*。当屏幕上有多个输入监听器,或要操作非触摸硬件时,请使用输入源前缀:

输入源典型用途
touchscreentap、swipe、draganddrop 的默认输入源。
touchpad外接触摸板(Chromebook / Android 平板键盘)。
dpad电视 / 焦点遍历:input dpad keyevent KEYCODE_DPAD_DOWN。
keyboardtext、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。

其他与测试相关的键
命名空间键用途
systemfont_scale浮点数(例如 1.0、1.3);用于字体缩放测试。
secureaccessibility_enabled设为 1 以启用无障碍功能;配合 enabled_accessibility_services 使用。
secureenabled_accessibility_services无障碍测试用的 CSV pkg/.Service。
securedefault_input_method强制使用已知的 IME。
secureshow_ime_with_hard_keyboard设为 1,使模拟器上的 IME 保持可见。
securelong_press_timeout稳定长按测试。
globalairplane_mode_on配合 am broadcast android.intent.action.AIRPLANE_MODE 使用。
globalpolicy_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 actionIntent 操作(例如 android.intent.action.VIEW)。
-d data_uri数据 URI。
-t mime_typeMIME 类型。
-c category类别。
-n component显式组件(pkg/.Activity)。
-f flags十六进制 setFlags() 值。
`--es-e key value`
--ez key bool布尔值 extra。
--ei key intInt extra。
--el key longLong extra。
--ef key floatFloat extra。
--eu key uriURI extra。
--ecn key componentComponentName 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 allowdeny
cmd jobscheduler run -f <pkg> <jobId>立即强制运行 JobService。
`cmd uimode night yesno
cmd locale set-app-locales <pkg> --locales en-US设置每个应用的语言区域(Android 13+)。
cmd statusbar expand-notifications / collapse展开/收起通知栏。
`cmd wifi set-wifi-enabled enableddisabled`
`cmd connectivity airplane-mode enabledisable`
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 而非插桩来操作。

评论

支持 Markdown