关于 connecting-to-devices
面向 Android Debug Bridge(ADB)开发者的实用技能,讲解设备发现、选择、就绪检查和常见连接问题,涵盖多设备及 Linux 权限场景。
主要功能
- 01列出设备并查看状态
- 02按序列号或传输 ID 选择设备
- 03等待传输连接及系统启动
- 04处理授权和 USB 权限问题
- 05在多台设备上运行命令
适用场景
- 01多个设备连接时指定目标
- 02在 CI 测试前等待 Android 启动
- 03排查 unauthorized 或 offline 状态
- 04修复 Linux 设备未出现在 adb devices 中
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ skydoves/ android-testing-skills --skill connecting-to-devices安装后在 Claude Code 中输入/connecting-to-devices确认已加载。
详细介绍
连接设备——列出、选择并等待传输连接
adb devices 是最常输入的 adb 命令。后续常见问题也确实存在:设备实际处于在线状态前脚本就开始运行,多设备 shell 操作因 more than one device/emulator 而失败,Linux 缺少 udev 规则,教程还会引用一个并不存在的 wait-for-device-online 用法。本技能记录了这些情况的准确信息。
何时使用本技能
- 用户连接了两台设备并运行
adb shell,结果收到error: more than one device/emulator。 - 用户报告设备在
adb devices中显示为unauthorized、offline或no permissions。 - 用户想让 CI 脚本等待设备完成启动后再运行测试。
- 用户从博客复制了
adb wait-for-device-online,运行后却看到用法说明报错。 - 用户使用 Linux,新插入的手机始终没有出现在
adb devices中。
何时不使用本技能
- 用户想了解高级架构、服务生命周期或环境变量——请使用
../../architecture/understanding-adb-architecture/SKILL.md。 - 用户想了解 Wi-Fi / 无线调试——请使用
../connecting-over-wifi/SKILL.md。 - 用户想在已连接的设备上安装应用或清除应用状态——请使用
../../apps/installing-and-managing-apps/SKILL.md。
前提条件
- 可正常工作的 ADB 服务(请参阅
../../architecture/understanding-adb-architecture/SKILL.md)。 - 已在
Settings → System → Developer options中启用 USB 调试 的设备。在 Android 4.2 及更高版本中,开发者选项默认隐藏,需在About phone中连续点按“Build number”七次才能显示。 - Linux:能够使用 sudo 写入
/etc/udev/rules.d/51-android.rules。 - Windows:厂商 USB 驱动(Pixel/Nexus 设备可通过 SDK Manager 获取 Google USB Driver;其他厂商设备需使用对应的 OEM 驱动)。
工作流
-
1. 每次都先运行
adb devices。 每个已连接的传输连接会占一行,格式为<serial> <state> [details...]。编写脚本时添加-l:adb devices -l # List of devices attached # emulator-5556 device product:sdk_google_phone_x86_64 model:Android_SDK_built_for_x86_64 device:generic_x86_64 transport_id:1 # 0a388e93 device usb:1-1 product:razor model:Nexus_7 device:flo transport_id:2-l会添加product、model、device、transport_id,以及(对于 USB 设备)USB 总线路径。 -
2. 根据情况选择正确的选择器:
标志 含义 失败情况 -s <serial>指定序列号。适用于 USB 序列号( 0a388e93)、模拟器(emulator-5554)和 TCP 设备(192.168.1.42:5555)。优先级高于$ANDROID_SERIAL。序列号未连接。 -d唯一的 USB 设备。 没有 USB 设备,或 USB 设备不止一个。 -e唯一的 TCP/IP 设备(包括模拟器和通过 adb connect连接的手机)。没有 TCP 设备,或 TCP 设备不止一个。 -t <transport_id>从 adb devices -l获取的数字传输 ID。在同一端口重新插拔后仍保持稳定。该传输 ID 不存在。 注意:
-t是传输 ID,不是超时标志。如果要限制单条命令的执行时间,请使用 shelltimeout(macOS 上使用gtimeout)包装命令。 -
3. 在脚本中筛选“就绪”的设备:
adb devices | awk '$2=="device"{print $1}'单设备快捷命令:
adb get-serialno返回序列号,adb get-state返回offline | bootloader | device之一,adb get-devpath返回 USB 路径。若要获取完整状态集,请直接解析adb devices。 -
4. 使用规范的
wait-for-*形式。你可能见过的其他形式都是错误的。 根据adb help(scripting部分),唯一有效的语法是:wait-for[-TRANSPORT]-STATE TRANSPORT ∈ { usb, local, any } (传输方式默认为 any) STATE ∈ { device, recovery, rescue, sideload, bootloader, disconnect }常见形式:
命令 阻塞直到…… adb wait-for-device任意传输方式下,设备进入 device状态。默认形式,常用于 CI 脚本开头。adb wait-for-usb-deviceUSB 连接的设备进入 device状态。adb wait-for-local-deviceTCP/IP 设备(模拟器或无线设备)进入 device状态。adb wait-for-bootloader设备进入 bootloader(fastboot)状态。adb wait-for-recovery/wait-for-sideload/wait-for-rescue对应设备进入这些模式。 adb wait-for-disconnect当前选中的设备断开连接(例如执行 adb reboot后)。文档未定义
wait-for-device-online形式。wait-for-*只会检查传输状态,不会检查启动是否完成;请配合轮询sys.boot_completed:adb wait-for-device until [[ "$(adb shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do sleep 1 done adb shell input keyevent 82 # 解锁 AOSP 上滑屏幕重启时的组合流程:
adb shell reboot adb wait-for-disconnect adb wait-for-device -
5. 正确读取设备状态。 根据 adb 页面(
Query for devices)和adb help:状态 含义 恢复方法 device在线, adbd有响应。不代表启动已完成;请检查sys.boot_completed。不适用 offline传输连接存在,但 adbd没有响应。常见于挂起/恢复或 USB 集线器之后。执行 adb reconnect offline,必要时重新插拔设备。unauthorized设备尚未接受主机 RSA 密钥。 解锁设备,然后在设备上的对话框中点按允许。如果没有出现对话框,请尝试换用其他 USB 线缆(某些“仅充电”线缆会阻断数据传输)。 recovery已启动进入 recovery。只有少部分 adb功能可用。不适用 sideloadOTA 侧载模式。使用 adb sideload package.zip。不适用 bootloader位于 bootloader(fastboot)中。 adb无法通信;请使用fastboot。不适用 rescueRescue Party 模式(Android 10+)。 不适用 connecting无线连接过程中的临时 TLS 握手状态。 等待。 no permissions(仅限 Linux)USB 设备可见,但因缺少 udev 规则而被阻止。 安装规则,参见第 7 步。 -
6. 首次连接时接受 RSA 指纹对话框。
“连接运行 Android 4.2.2(API 级别 17)或更高版本的设备时,系统会显示一个对话框,询问是否接受 RSA 密钥,以允许通过这台计算机进行调试。” — developer.android.com/tools/adb 流程:
adbd检测到一个提供 RSA 公钥的新客户端。- 操作系统会弹出“允许 USB 调试?”对话框,其中显示工作站的密钥指纹。
- 在用户点按允许之前,
adb devices会报告unauthorized。 - 接受后,公钥会追加到设备上的
/data/misc/adb/adb_keys。勾选始终允许这台计算机,这样今后从同一个~/.android/adbkey连接时就不会再提示。
可通过
Settings → Developer options → Revoke USB debugging authorizations撤销旧工作站的授权。 -
7.(Linux)安装 udev 规则,避免新连接的手机卡在
no permissions。规范文件:/etc/udev/rules.d/51-android.rules。最简示例(每个 OEM 一行):# Google SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0660", GROUP="plugdev", TAG+="uaccess" # Samsung SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", MODE="0660", GROUP="plugdev", TAG+="uaccess"无需重启即可应用:
sudo udevadm control --reload-rules sudo udevadm trigger # unplug and re-plug the device大多数发行版也会提供综合性的
android-udev/android-sdk-platform-tools-common软件包,将同一文件放在/lib/udev/rules.d/中。 -
**8.(Windows)安装厂商 USB 驱动。**Pixel / Nexus / 通用 AOSP 设备使用
SDK Manager → SDK Tools → Google USB Driver中的 Google USB Driver(文件位于android_sdk\extras\google\usb_driver\)。其他 OEM 需要从https://developer.android.com/tools/extras/oem-usb获取制造商驱动。macOS 和 Linux 无需厂商驱动。 -
**9. 多设备扇出。**ADB 没有内置的
--all标志。有两种模式:# Sequential for s in $(adb devices | awk '$2=="device"{print $1}'); do adb -s "$s" install -r app.apk done # Parallel (4 jobs at a time) adb devices | awk '$2=="device"{print $1}' \ | xargs -I{} -P 4 adb -s {} install -r app.apk对于各自连接一部手机的分片 CI 运行器,将服务锁定到单台设备:
adb --one-device 0a388e93 start-server
模式
模式:错误做法 vs 正确做法 — more than one device/emulator
# WRONG
adb shell getprop ro.product.model
# adb: more than one device/emulator
# WRONG because: with two transports attached (e.g. an emulator + a USB phone),
# every non-server adb command needs an explicit selector. The command did not
# fail; it never even ran.
# RIGHT
adb -s emulator-5554 shell getprop ro.product.model
# Or, if there is exactly one USB phone:
adb -d shell getprop ro.product.model
# Or set $ANDROID_SERIAL once and stop typing -s:
export ANDROID_SERIAL=emulator-5554
adb shell getprop ro.product.model
模式:错误做法 vs 正确做法 — 虚构的 wait-for-device-online
# WRONG
adb wait-for-device-online
# error: usage: ...
# WRONG because: this command does not exist. The canonical syntax is
# wait-for[-TRANSPORT]-<state> with state in {device, recovery, rescue,
# sideload, bootloader, disconnect}. There is no -online state.
# RIGHT
adb wait-for-device # any transport, state=device
# Then poll boot complete (transport up != system booted):
until [[ "$(adb shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do
sleep 1
done
模式:错误做法 vs 正确做法 — 模拟器启动竞态
# WRONG
emulator -avd Pixel_API_34 &
adb install -r app.apk # races: emulator may not be online yet
# Often: error: device 'emulator-5554' not found
# RIGHT
adb start-server # important: see corner case below
emulator -avd Pixel_API_34 &
adb -s emulator-5554 wait-for-device
until [[ "$(adb -s emulator-5554 shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do
sleep 1
done
adb -s emulator-5554 install -r app.apk
边缘情况(逐字摘自 adb 页面):“在以下条件全部成立时,[可能]不会在
adb devices中显示正在运行的模拟器……:adb 服务未运行;你使用带有-port或-ports选项的emulator命令,且端口号为 5554 到 5584 之间的奇数;……你在启动模拟器后才启动 adb 服务。”解决方法:在启动emulator之前运行adb start-server。
模式:错误做法 vs 正确做法 — transport_id 与 -t 混淆
# WRONG
adb -t 60 shell getprop ro.product.model
# WRONG because: -t is transport_id, not a 60-second timeout. Either there is
# no transport with id=60 (error: device not found) or the command runs against
# the wrong device.
# RIGHT
# Real timeout via the shell tool:
timeout 60 adb -s emulator-5554 shell getprop ro.product.model # GNU timeout
gtimeout 60 adb -s emulator-5554 shell getprop ro.product.model # macOS (coreutils via brew)
# Use -t only with a transport_id from adb devices -l:
adb -t 2 shell getprop ro.product.model
强制规则
- 必须在连接了多个传输设备时,为每条 adb 命令添加选择器(
-s/-d/-e/-t),或设置$ANDROID_SERIAL。 - 必须使用文档规定的
wait-for[-TRANSPORT]-<state>语法。不存在wait-for-device-online。 - 必须在
wait-for-device后轮询getprop sys.boot_completed,前提是脚本需要等待操作系统完全启动(例如,在安装 APK 或调用am instrument之前)。 - 不得将
-t TRANSPORT_ID与超时标志混淆。需要实际超时时,请使用timeout/gtimeout封装。 - 不得为了“修复”
unauthorized而删除~/.android/adbkey*——正确的恢复方法请参见../../architecture/understanding-adb-architecture/SKILL.md。 - **优先:**对于可能遇到两台序列号相同设备的脚本(罕见的硬件错误或重复的 AVD),使用
adb devices -l和transport_id。 - **优先:**在 Linux 上安装发行版的
android-udev软件包,而不是手动维护51-android.rules。
验证
-
adb devices -l会列出每个已连接的传输,并显示状态device(不能是offline、unauthorized或no permissions)。 -
adb devices | awk '$2=="device"{print $1}'会为每台就绪的设备返回一行。 -
adb -s <serial> get-state会打印device。 - 至少有一个传输处于
device状态时,adb wait-for-device会立即返回。 - 全新启动后,
adb shell getprop sys.boot_completed会打印1。 -
adb -d shell echo ok(或-e、-s)会成功执行,不会提示more than one device/emulator。 - 在 Linux 上,
lsusb会显示设备;应用 udev 规则后,adb devices会报告device(而非no permissions)。
参考资料
- ADB 用户指南(
adb devices、wait-for-*、RSA 对话框):https://developer.android.com/tools/adb - 在设备上运行的设置(Linux udev 规则和 Windows 驱动程序):https://developer.android.com/studio/run/device
- OEM USB 驱动程序(Windows):https://developer.android.com/tools/extras/oem-usb
- AOSP
adb手册页:https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/docs/user/adb.1.md tasks/research/A1-adb-architecture-devices.md—wait-for-*表格原文、wait-for-device-online更正、完整状态集、udev 规则配置步骤。docs/CORPUS.md§I.3(状态真值集)和 §I.10(研究结果)。- 同系列技能:
- 高层架构:
../../architecture/understanding-adb-architecture/SKILL.md - 无线 ADB:
../connecting-over-wifi/SKILL.md - 安装 / 清除应用:
../../apps/installing-and-managing-apps/SKILL.md - 运行插桩测试:
../../tests/running-instrumented-tests-via-adb/SKILL.md
- 高层架构:
- 跨系列相关内容:
- 使用
AndroidJUnit4运行插桩测试:../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.md - 在 Android 上配置 JUnit4:
../../../jvm-tests/runner/configuring-junit4-on-android/SKILL.md - 源集策略:
../../../fundamentals/strategies/organizing-test-source-sets/SKILL.md
- 使用