mcpField
C
connecting-to-devices

connecting-to-devices

github.com/skydoves开发33969 次安装22 周前更新
代码仓库

关于 connecting-to-devices

面向 Android Debug Bridge(ADB)开发者的实用技能,讲解设备发现、选择、就绪检查和常见连接问题,涵盖多设备及 Linux 权限场景。

主要功能

  1. 01列出设备并查看状态
  2. 02按序列号或传输 ID 选择设备
  3. 03等待传输连接及系统启动
  4. 04处理授权和 USB 权限问题
  5. 05在多台设备上运行命令

适用场景

  1. 01多个设备连接时指定目标
  2. 02在 CI 测试前等待 Android 启动
  3. 03排查 unauthorized 或 offline 状态
  4. 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,不是超时标志。如果要限制单条命令的执行时间,请使用 shell timeout(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 流程:

    1. adbd 检测到一个提供 RSA 公钥的新客户端。
    2. 操作系统会弹出“允许 USB 调试?”对话框,其中显示工作站的密钥指纹。
    3. 在用户点按允许之前,adb devices 会报告 unauthorized。
    4. 接受后,公钥会追加到设备上的 /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

评论

支持 Markdown