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.x192.168.x.x10.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 deviceDevice IPState=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 addrinet
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 无线调试。真正麻烦的不是命令本身,而是多设备歧义、状态误判、编码乱码、非交互延时这些细节------它们每一处都值得在脚本里提前处理好。

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

相关推荐
guwentian2 小时前
Rolldown vs esbuild vs Webpack:Rust 打包器大战我们该怎么选
前端·webpack·rust·esbuild
特立独行的猫A3 小时前
用纯 Rust 重写 Podcastfy:把网页、PDF 和一段文字,变成一档双人对话播客
前端
oooo_z3 小时前
美股历史K线数据API接口选型指南:iTick覆盖1分钟到月线全周期
服务器·前端·javascript
律宏阔3 小时前
Android WebRTC + H.265 适配记录
android
今日无bug3 小时前
从 0 到 1 搭建端侧 AI 项目:DeepSeek-R1 + WebGPU + React + TS + Tailwind 全栈笔记
前端·react.js·typescript
mmsx3 小时前
MapLibre 实战 08|GPS 点漂了几百米才被发现:GCJ-02 纠偏原理与"转两次"陷阱
android·前端
春夏与冬3 小时前
Android : apktool
android
YF02113 小时前
如何验证App预置为特权App功能正常?
android
李高钢3 小时前
Python Flask 框架入门:从零搭建你的第一个 Web 应用
前端·python·flask