Android 手簿 ADB 无线调试全攻略:USB 转 WiFi 一键连接

Android 手簿 ADB 无线调试全攻略:USB 转 WiFi 一键连接

面向现场测绘 / 硬件工程师的一篇实操向技术博客。解决「全站仪手簿、安卓主机等设备调试时被 USB 线束缚」的痛点,用 adb tcpip + 自动读 IP + 重试,实现插一次 USB、之后全程 WiFi 无线调试。


目录

  1. 使用前提
  2. 原理解析
  3. 一键脚本
  4. 脚本关键点拆解
  5. 使用步骤
  6. 常见问题排查
  7. 总结

一、使用前提

开始之前,请逐条确认下面的前置条件。任何一条不满足,都会导致脚本卡在某个环节。

1. 硬件准备

项目 要求
目标设备 Android 设备(手簿 / 全站仪主机),系统版本不限(本文方案兼容较老版本)
USB 数据线 支持数据传输(非纯充电线),且手簿已能通过 USB 被电脑识别
电脑 Windows(本文脚本为 bat / ps1)
网络 设备与电脑需处于同一局域网(同一 WiFi / 同一路由器下)

2. 软件环境

  • 电脑已安装 adb(Android SDK platform-tools)。
  • adb 已加入系统 PATH,在任意终端输入 adb version 能看到版本号。
  • 验证命令:
bat 复制代码
adb version

如果提示「不是内部或外部命令」,需要把 platform-tools 目录加到 PATH,或修改脚本顶部的 ADB 变量为完整路径(如 D:\platform-tools\adb.exe)。

3. 设备端配置

在手簿 / 主机上依次完成:

  1. 打开 设置 → 关于设备 ,连点「版本号」7 次,开启 开发者选项。
  2. 进入 开发者选项 ,打开 USB 调试(USB debugging)。
  3. 建议同时打开「USB 安装」「保持唤醒」,避免调试中途休眠断开。

4. 网络要求(最关键)

  • 设备必须已连接 WiFi,且拿到了局域网 IP。
  • 电脑与设备必须能互相 ping 通 。例如设备 IP 是 172.16.93.76,在电脑上:
bat 复制代码
ping 172.16.93.76
  • 常见网段:172.16.x.x、192.168.x.x、10.x.x.x。如果 ping 不通,先解决网络,再谈无线调试。

5. 授权确认

  • 第一次通过 USB 连接时,设备屏幕会弹出「是否允许 USB 调试 」,必须点允许(并勾选「一律允许」)。
  • 如果之前点过「拒绝」,需要到开发者选项里「撤销 USB 调试授权」后重新插线确认。

二、原理解析

adb 本质上是一个 C/S 架构 :电脑端是 adb 客户端,设备端跑着 adbd 守护进程。连接方式有两种:

方式 传输层 设备标识
USB 调试 USB 线(transport-usb) 序列号,如 ABCD1234
无线调试 TCP(transport-tcp) IP:端口,如 172.16.93.76:5555

整套方案的核心只有三步:

text 复制代码
1. adb tcpip 5555
   让设备端的 adbd 从「USB 模式」切换到「TCP 监听模式」,开始监听 5555 端口。

2. 读取设备 WiFi 的 IP
   通过 USB 通道(此时 USB 还连着)执行 adb shell ip addr,取出 wlan0 的 IP。

3. adb connect <ip>:5555
   电脑端主动向该 IP 的 5555 端口发起 TCP 连接,建立无线调试通道。

之后即可拔掉 USB 线,只要设备 IP 不变、网络不断,无线调试一直有效。

注意:tcpip 会重启设备端 adbd,所以重启后到端口真正可连之间有几秒的间隔,这也是脚本里需要加延时 / 重试的原因。


三、一键脚本

下面是两个完整版本,任选其一。脚本都已处理「多设备、重试、状态判定、编码」等踩坑点(详见第四节)。

版本一:批处理脚本 adb_wifi_connect.bat

保存为 .bat,双击或命令行运行。

bat 复制代码
@echo off
setlocal enabledelayedexpansion

title ADB WiFi Connect

set "ADB=adb"
set "PORT=5555"
set "MAX_RETRY=3"

echo ==============================================
echo   ADB WiFi Connect (retry up to %MAX_RETRY% times)
echo ==============================================
echo.

%ADB% version >nul 2>&1
if errorlevel 1 (
    echo [ERROR] adb not found. Add it to PATH or set ADB to full path.
    pause
    exit /b 1
)

rem --- 0. Disconnect old WiFi connections to avoid "more than one device" ---
echo [0/4] Disconnecting old WiFi connections ...
%ADB% disconnect >nul 2>&1

rem --- detect USB device serial (the line without a colon) ---
set "USB_SERIAL="
set "USB_COUNT=0"
for /f "tokens=1" %%s in ('%ADB% devices ^| findstr /v "List of"') do (
    echo %%s | findstr ":" >nul
    if errorlevel 1 (
        set /a USB_COUNT+=1
        set "USB_SERIAL=%%s"
    )
)

if "%USB_COUNT%"=="0" (
    echo [ERROR] No USB device found. Plug in the device via USB.
    pause
    exit /b 1
)
if not "%USB_COUNT%"=="1" (
    echo [ERROR] %USB_COUNT% USB devices found. Unplug the extras and retry.
    pause
    exit /b 1
)
echo USB device: %USB_SERIAL%

rem --- 1. tcpip on the specific USB device ---
echo [1/4] adb -s %USB_SERIAL% tcpip %PORT% ...
%ADB% -s %USB_SERIAL% tcpip %PORT%
if errorlevel 1 (
    echo [ERROR] adb tcpip failed. Check USB connection and USB debugging.
    pause
    exit /b 1
)

ping -n 3 127.0.0.1 >nul

rem --- 2. read device IP ---
echo [2/4] Reading device IP ...
set "DEVICE_IP="
call :get_ip %USB_SERIAL% wlan0
if not defined DEVICE_IP call :get_ip %USB_SERIAL% eth0
if not defined DEVICE_IP call :get_ip %USB_SERIAL% ""

if not defined DEVICE_IP (
    echo [ERROR] Cannot read device IP. Make sure device is on WiFi.
    pause
    exit /b 1
)
echo Device IP: %DEVICE_IP%

rem --- 3. connect with retry ---
echo [3/4] Connecting to %DEVICE_IP%:%PORT% ...

set /a TRY=0
:connect_loop
set /a TRY+=1
echo   Attempt %TRY%/%MAX_RETRY% ...
%ADB% connect %DEVICE_IP%:%PORT%

set "STATE="
for /f "tokens=1,2" %%d in ('%ADB% devices ^| findstr /c:"%DEVICE_IP%:%PORT%"') do set "STATE=%%e"

echo   State=[%STATE%]

if /i "%STATE%"=="device" goto :connected

if %TRY% LSS %MAX_RETRY% (
    echo   Not ready, retrying in 2s ...
    ping -n 3 127.0.0.1 >nul
    goto :connect_loop
)

echo.
echo [FAILED] Cannot connect after %MAX_RETRY% attempts.
echo Current adb devices:
%ADB% devices
pause
exit /b 1

:connected
echo   Connected!
echo.
echo Current adb devices:
%ADB% devices
echo.
echo ==============================================
echo   Done. Now you can unplug the USB cable.
echo   Disconnect: %ADB% disconnect %DEVICE_IP%:%PORT%
echo ==============================================
pause
exit /b 0

rem ---------- sub: get IPv4 by interface ----------
:get_ip
set "SERIAL=%~1"
set "IFACE=%~2"
if "%IFACE%"=="" (
    for /f "tokens=2 delims= " %%a in ('%ADB% -s %SERIAL% shell ip -4 addr 2^>nul ^| findstr "inet"') do (
        call :pick_ip %%a
        if defined DEVICE_IP goto :eof
    )
) else (
    for /f "tokens=2 delims= " %%a in ('%ADB% -s %SERIAL% shell ip -4 addr show %IFACE% 2^>nul ^| findstr "inet"') do (
        call :pick_ip %%a
        if defined DEVICE_IP goto :eof
    )
)
goto :eof

rem ---------- sub: strip /mask, skip loopback ----------
:pick_ip
set "RAW=%~1"
for /f "tokens=1 delims=/" %%b in ("%RAW%") do set "IP=%%b"
if not "%IP%"=="127.0.0.1" set "DEVICE_IP=%IP%"
goto :eof

版本二:PowerShell 脚本 adb_wifi_connect.ps1

运行方式:powershell -ExecutionPolicy Bypass -File adb_wifi_connect.ps1

powershell 复制代码
$ErrorActionPreference = "Stop"

$adb      = "adb"
$port     = 5555
$maxRetry = 3

Write-Host "=== ADB WiFi Connect (retry up to $maxRetry times) ===" -ForegroundColor Cyan

# 0. Disconnect old WiFi connections to avoid "more than one device"
Write-Host "[0/4] Disconnecting old WiFi connections ..."
& $adb disconnect | Out-Null

# detect USB devices (serial without a colon)
$usbDevices = @()
foreach ($line in (& $adb devices)) {
    if ($line -and $line -notmatch 'List of devices') {
        $parts  = $line -split '\s+'
        $serial = $parts[0]
        if ($serial -notmatch ':') {
            $usbDevices += $serial
        }
    }
}

if ($usbDevices.Count -eq 0) {
    Write-Host "[ERROR] No USB device found. Plug in the device via USB." -ForegroundColor Red
    exit 1
}
if ($usbDevices.Count -gt 1) {
    Write-Host "[ERROR] $($usbDevices.Count) USB devices found. Unplug the extras and retry." -ForegroundColor Red
    exit 1
}
$serial = $usbDevices[0]
Write-Host "USB device: $serial"

# 1. tcpip on the specific USB device
Write-Host "[1/4] adb -s $serial tcpip $port ..."
& $adb -s $serial tcpip $port
Start-Sleep -Seconds 2

# 2. read device IP
Write-Host "[2/4] Reading device IP ..."
$ip = $null
foreach ($iface in @("wlan0", "eth0")) {
    $out = & $adb -s $serial shell ip -4 addr show $iface 2>$null
    $line = $out | Where-Object { $_ -match 'inet\s+\d+\.\d+\.\d+\.\d+' } | Select-Object -First 1
    if ($line -match 'inet\s+(\d+\.\d+\.\d+\.\d+)') {
        $ip = $Matches[1]
        break
    }
}
if (-not $ip) {
    $out = & $adb -s $serial shell ip -4 addr 2>$null
    $line = $out | Where-Object { $_ -match 'inet\s+\d+\.\d+\.\d+\.\d+' -and $_ -notmatch '127\.0\.0\.1' } | Select-Object -First 1
    if ($line -match 'inet\s+(\d+\.\d+\.\d+\.\d+)') {
        $ip = $Matches[1]
    }
}

if (-not $ip) {
    Write-Host "[ERROR] Cannot read device IP. Make sure the device is connected to WiFi." -ForegroundColor Red
    exit 1
}
Write-Host "Device IP: $ip"

# 3. connect with retry. Only 'device' state counts as success.
Write-Host "[3/4] Connecting to ${ip}:${port} ..."
$connected = $false
for ($i = 1; $i -le $maxRetry; $i++) {
    Write-Host "  Attempt $i/$maxRetry ..."
    & $adb connect "${ip}:${port}" | Out-Host

    $state = $null
    foreach ($line in (& $adb devices)) {
        if ($line -like "${ip}:${port}*") {
            $parts = $line -split '\s+'
            if ($parts.Count -ge 2) { $state = $parts[1] }
        }
    }
    Write-Host "  State: $state"

    if ($state -eq 'device') {
        $connected = $true
        break
    }

    if ($i -lt $maxRetry) {
        Write-Host "  Not ready, retrying in 2s ..."
        Start-Sleep -Seconds 2
    }
}

Write-Host ""
Write-Host "Current adb devices:"
& $adb devices

if (-not $connected) {
    Write-Host ""
    Write-Host "[FAILED] Cannot connect after $maxRetry attempts." -ForegroundColor Red
    exit 1
}

Write-Host ""
Write-Host "Done. Now you can unplug the USB cable." -ForegroundColor Green
Write-Host "Disconnect: adb disconnect ${ip}:${port}"

四、脚本关键点拆解

脚本不是简单地把三条命令串起来,下面这些点都是实测踩坑后的沉淀。

1. 多设备歧义:more than one device

如果之前跑过一次脚本,残留的 WiFi 连接(172.16.93.76:5555)还在 adb devices 列表里,此时再加 USB,就是两个设备。直接 adb tcpip 5555 会报:

text 复制代码
error: more than one device/emulator

解决 :脚本开头先 adb disconnect 断开所有 TCP 连接(只留 USB),再从 adb devices 里解析出「不含冒号」的那行作为 USB 序列号,后续用 adb -s <serial> 锁定设备。

2. 成功判定:只认 device

adb connect 后,adb devices 里对应条目的状态有三种:

状态 含义 是否可用
device 已授权、可调试 ✅
unauthorized 连上了但没授权 ❌(需在设备上点允许)
offline TCP 通了但 adbd 没响应 ❌

早期版本把 unauthorized 也当成功,导致误报「已连接」。正确做法是只认 device,其余都重试。

3. 自动读 IP:接口优先级

不同设备网卡名不同,脚本按 wlan0 → eth0 → 遍历所有接口 的顺序尝试,最后统一排除回环地址 127.0.0.1。这样 WiFi 手簿、有线主机都能适配,且不硬编码 IP(DHCP 会漂移)。

4. 延时用 ping 而非 timeout

timeout /t 在非交互终端(IDE 终端、管道调用)会报错:

text 复制代码
ERROR: Input redirection is not supported, exiting the process immediately.

导致脚本中断、看不到进度。改用 ping -n 3 127.0.0.1 >nul 做约 2 秒延时,不依赖 stdin,任何环境都能跑。

5. 提示用英文而非中文

Windows 自带 PowerShell 5.1 会把无 BOM 的 UTF-8 文件按 GBK 解码,中文提示在脚本加载阶段就变成乱码,且运行期改编码无效。因此脚本提示统一用英文,彻底规避编码问题。


五、使用步骤

  1. 确认前提:逐条核对「一、使用前提」。
  2. 插线:USB 数据线连接手簿与电脑。
  3. 跑脚本 :双击 adb_wifi_connect.bat,或执行 ps1。
  4. 观察输出 :确认 USB device、Device IP、State=device 都正常。
  5. 拔线 :拔掉 USB,此时 adb devices 里仍有 172.16.93.76:5555 device 即成功。
  6. 验证 :adb shell 能进入设备 shell,即无线调试生效。

六、常见问题排查

现象 原因 处理
error: more than one device/emulator 残留 WiFi 连接 + USB 共 2 个设备 脚本已自动 adb disconnect;或手动执行 adb disconnect
adb tcpip failed USB 未识别 / 未开 USB 调试 检查数据线、开发者选项、USB 调试授权
Cannot read device IP 设备没连 WiFi / 没拿到 IP 让设备连上 WiFi,确认 adb shell ip addr 有 inet
State: unauthorized 设备端没点「允许调试」 手簿屏幕上点允许;或撤销授权后重插
State: offline 电脑与设备不在同一网段 / 网络不通 ping 设备 IP,确保同网段、防火墙放行
昨天能连今天连不上 设备 DHCP 换 IP 了 重跑脚本(自动读新 IP);或给设备配静态 IP
ps1 输出乱码 PowerShell 5.1 编码问题 改用英文版;或用 bat 版
bat 看不到进度 timeout 在非交互终端中断 已改用 ping 延时

七、总结

adb tcpip + 自动读 IP + adb connect 三招,就能把手簿从 USB 线里解放出来,实现全程 WiFi 无线调试。真正麻烦的不是命令本身,而是多设备歧义、状态误判、编码乱码、非交互延时这些细节------它们每一处都值得在脚本里提前处理好。

希望这篇博客能帮你少踩几个坑。

相关推荐
执明wa7 小时前
Android RecyclerView 实战:点击频道栏切换对应内容
android·开发语言·windows·microsoft·android studio
web打印社区7 小时前
JS 静默打印怎么做:纯前端为什么不行,以及最小可跑通写法
开发语言·前端·javascript·websocket·网络协议·http·pdf
web打印社区7 小时前
HTML5 静默打印:前端页面怎么调用打印机且尽量不弹窗
前端·javascript·vue.js·pdf·html·html5
Python大数据分析7 小时前
开源免费、AI 驱动的 Web 打印设计器 OpenPrint:从拖拽设计到 ERP 对接全流程实战
前端·人工智能·开源
新鲜势力呀8 小时前
PHP 定时数据同步实战:从第三方接口超时到异步同步 + 增量更新架构优化全过程
android
溪语流沙8 小时前
【每天一个CSS | Day11】赛博朋克故障风文字:红青分离 + 切片抖动
前端·css
liangshanbo12158 小时前
React Fiber:从“解决卡顿”到完整更新链路
前端·javascript·react.js
Timmy8 小时前
前端转全栈笔记:讲框架之前,先把 TypeScript 这关过了
前端·人工智能·node.js
计算机魔术师8 小时前
年收入650亿、亏420亿,Anthropic凭什么敢叫价万亿?
前端
Frag0ut8 小时前
Chrome Canary 最新实验功能:AI Mode、按设备筛选历史记录与 Gemini 自动改密
前端·人工智能·chrome·dev·beta·canary·最新实验功能