ADB (Android Debug Bridge) 详解

不管你是搞App爬虫自动化、逆向分析,还是做Android原生/跨端开发,ADB(Android Debug Bridge)绝对是包里压箱底的「移动设备瑞士军刀」。不用零散查命令踩坑,今天这篇从0授权连接、WiFi调试,到Python批量自动化,一次性覆盖核心实用功能。


一、设备管理篇:搞定连接第一步

新手最容易卡「unauthorized(未授权)」「offline(离线)」「空设备列表」这三个坎,先把连接前置、检查、WiFi配对这三步讲明白。

1.1 授权设置(避坑核心)

不管USB还是首次WiFi,手机必须弹出「允许USB调试」授权弹框才能操作:

  • 数据线要求:别用只供电的充电线!必须用「充电+数据传输二合一」的
  • 手机操作链:「设置 → 关于手机 → 连续点击版本号7次(直到提示已开启开发者选项)」→ 返回「设置 → 系统(或更多设置)→ 开发者选项」→ 打开「USB调试」「USB调试安全设置」
  • 弹框处理:勾选「始终允许此计算机」→ 点击「确定」,别漏勾!

如果弹框死活不出来,用「重启ADB服务+重启手机USB调试开关」的组合拳:

# 停止ADB服务(解决offline、授权未生效的万能第一步)
adb kill-server
# 重启ADB服务
adb start-server

1.2 基础连接检查

# 查看所有已连接设备的ID和状态
# 状态说明:device=正常,unauthorized=未授权,offline=离线
adb devices

# 只提取正常设备ID(适合脚本直接调用)
adb devices -l | awk 'NR>1 && /device/ && !/offline/ {print $1}'

1.3 WiFi无线调试(彻底解放数据线)

前置条件:手机和电脑在同一局域网(不用翻墙,连同一个WiFi/路由器就行)

# 1. 先用USB线连接设备,确保adb devices显示device
# 2. 开启手机TCP/IP调试端口(默认5555,自定义也可以,建议留默认)
adb tcpip 5555
# 3. 查询手机的局域网IP(3种常用方法,优先试前两种)
## 方法1:通用SDK命令(适配大部分机型)
adb shell getprop dhcp.wlan0.ipaddress
## 方法2:查看WLAN0网卡(如果没输出,试试eth0/rmnet_data0)
adb shell ifconfig wlan0
## 方法3:WiFi专用接口(Android 10+更准确)
adb shell cmd connectivity wifi status | grep "IpAddress"
# 4. 用IP+端口连接(替换成你手机的实际IP)
adb connect 192.168.1.100:5555
# 5. 拔掉USB线,再次检查adb devices确认状态为device

二、文件传输篇:无压缩高速互传

不用微信/QQ传文件(压缩画质、限速还要扫码),ADB传输速度取决于数据线/局域网带宽,适合传APK、日志、测试数据:

# 推本地文件到手机
# 优先用/sdcard/开头的路径(用户可读写的公共存储,无需root)
adb push ./test_apk.apk /sdcard/
# 推整个文件夹,加-r参数(递归)
adb push -r ./crawler_temp /sdcard/crawler_backup/

# 拉手机文件到本地
# 本地路径可以是相对路径,也可以是绝对路径
adb pull /sdcard/logcat_latest.txt ./
adb pull -r /sdcard/crawler_backup/ ./local_data/

# 快速同步(只同步修改过的文件,比全量push/pull快很多)
adb sync  # 默认同步/sdcard/等公共分区
adb sync /system  # 同步系统分区,需root权限

三、应用控制篇:App爬虫的刚需基础

3.1 安装/卸载应用

# 安装APK
## 普通安装
adb install ./test_apk.apk
## 覆盖安装(保留数据,适合版本迭代调试)
adb install -r ./test_apk_v2.apk
## 允许降级安装(应用签名一致即可,部分Android版本无需root)
adb install -d ./test_apk_v1.apk
## 安装到SD卡(仅部分旧机型支持)
adb install -s ./test_apk.apk

# 卸载应用
## 普通卸载(保留缓存数据)
adb uninstall com.example.testapp
## 彻底卸载(删除所有数据和缓存,App爬虫/重置测试常用)
adb uninstall -k com.example.testapp

3.2 应用信息查询(找包名/启动项必备)

# 列出所有已安装的包(加参数快速筛选,不用翻手机)
adb shell pm list packages       # 所有包(系统+第三方)
adb shell pm list packages -3    # 仅第三方应用(App爬虫99%用这个!)
adb shell pm list packages -s    # 仅系统应用
adb shell pm list packages -f    # 显示包对应的APK路径(找逆向分析的目标APK)

# 查看某应用的详细信息(包名必须准确)
adb shell dumpsys package com.example.testapp
## 快速提取常用信息(用grep过滤,不用翻几百行输出)
### 版本号
adb shell dumpsys package com.example.testapp | grep versionName
### 版本代码
adb shell dumpsys package com.example.testapp | grep versionCode
### 启动Activity(找带LAUNCHER+DEFAULT的那个,比monkey模拟更稳)
adb shell dumpsys package com.example.testapp | grep -A 5 "android.intent.category.LAUNCHER"

3.3 启动/停止应用

# 停止应用(不管前台后台,彻底杀掉进程,不留后台驻留)
adb shell am force-stop com.example.testapp

# 启动应用
## 方法1:知道启动Activity时用(最稳定,不会误触其他应用)
adb shell am start -n com.example.testapp/.MainActivity
## 方法2:不知道启动Activity时用(monkey模拟点击应用图标启动)
adb shell monkey -p com.example.testapp -c android.intent.category.LAUNCHER 1

四、Python自动化控制:解放双手写爬虫

手动敲单条命令没问题,但批量操作(比如安装10个测试APK、每10秒截一次屏存证)用Python封装成类更高效。这里用Python内置的subprocess模块实现,无需安装第三方依赖,轻量且稳定。

import subprocess
import time
import os
from typing import Optional, List

class LightADB:
    """轻量级ADB控制器 - 专注App爬虫/自动化基础操作"""
    
    def __init__(self, device_id: Optional[str] = None, timeout: int = 30):
        """
        初始化ADB控制器
        :param device_id: 指定设备ID(多设备同时连接时必填)
        :param timeout: 单条ADB命令超时时间(秒)
        """
        self.device_id = device_id
        self.timeout = timeout
        self.available_devices = self._fetch_devices()
        self.current_device = self._select_current_device()
        print(f"✅ ADB初始化完成,当前操作设备: {self.current_device}")
    
    def _fetch_devices(self) -> List[str]:
        """内部方法:获取所有正常在线的设备ID"""
        try:
            result = subprocess.run(
                ['adb', 'devices'],
                capture_output=True, text=True, timeout=self.timeout
            )
            # 跳过标题行,只保留状态为device的设备
            raw_devices = result.stdout.strip().split('\n')[1:]
            return [
                line.split('\t')[0]
                for line in raw_devices
                if line.strip() and 'device' in line and 'offline' not in line
            ]
        except subprocess.TimeoutExpired:
            print("⚠️ ADB命令超时,请检查服务是否正常")
            return []
        except Exception as e:
            print(f"❌ 获取设备列表失败: {str(e)}")
            return []
    
    def _select_current_device(self) -> str:
        """内部方法:自动或手动选择当前设备"""
        if not self.available_devices:
            raise Exception("❌ 未找到可用ADB设备,请检查连接/授权")
        
        if self.device_id and self.device_id in self.available_devices:
            return self.device_id
        # 单设备默认选第一个
        return self.available_devices[0]
    
    def _exec_cmd(self, cmd_list: List[str]) -> str:
        """
        内部方法:执行带设备ID的ADB命令
        :param cmd_list: 不带adb前缀的命令列表(比如['install', 'xxx.apk'])
        :return: 命令执行后的标准输出,失败返回空字符串
        """
        full_cmd = ['adb', '-s', self.current_device] + cmd_list
        try:
            result = subprocess.run(
                full_cmd,
                capture_output=True, text=True, timeout=self.timeout
            )
            if result.returncode != 0:
                print(f"⚠️ 命令执行失败: {' '.join(full_cmd)}\n错误: {result.stderr.strip()}")
                return ""
            return result.stdout.strip()
        except Exception as e:
            print(f"❌ 命令执行异常: {str(e)}")
            return ""
    
    # ------------------- 以下是常用的公开方法 -------------------
    def get_third_packages(self) -> List[str]:
        """获取所有第三方应用的包名(App爬虫首选)"""
        output = self._exec_cmd(['shell', 'pm', 'list', 'packages', '-3'])
        return [line.split(':')[1] for line in output.split('\n') if line.strip()]
    
    def capture_screen(self, save_dir: str = "./adb_screenshots") -> Optional[str]:
        """
        截屏并保存到本地
        :param save_dir: 本地保存目录(自动创建)
        :return: 保存成功返回文件路径,失败返回None
        """
        os.makedirs(save_dir, exist_ok=True)
        timestamp = int(time.time())
        temp_phone_path = f"/sdcard/adb_temp_{timestamp}.png"
        local_path = os.path.join(save_dir, f"screen_{timestamp}.png")
        
        # 1. 在手机上截屏
        self._exec_cmd(['shell', 'screencap', temp_phone_path])
        # 2. 拉取到本地
        pull_res = self._exec_cmd(['pull', temp_phone_path, local_path])
        # 3. 删除手机上的临时文件
        self._exec_cmd(['shell', 'rm', temp_phone_path])
        
        if "pulled" in pull_res.lower():
            print(f"✅ 截屏已保存: {local_path}")
            return local_path
        return None

# ------------------- 使用示例 -------------------
if __name__ == "__main__":
    try:
        adb = LightADB()
        
        # 1. 获取前3个第三方应用
        third_pkgs = adb.get_third_packages()[:3]
        print(f"📱 前3个第三方应用: {third_pkgs}")
        
        # 2. 连续3次截屏,间隔2秒
        for i in range(3):
            adb.capture_screen()
            if i < 2:
                time.sleep(2)
                
    except Exception as e:
        print(f"❌ 示例运行失败: {str(e)}")

五、补充:几个实用小技巧

5.1 端口转发(本地服务/调试代理场景)

把手机的端口映射到电脑上,比如手机上运行了一个本地API服务在5000端口,电脑访问localhost:6000就能直接连到手机:

# 格式:adb forward 电脑端口 手机端口
adb forward tcp:6000 tcp:5000

# 取消所有端口转发
adb forward --remove-all

5.2 清空日志

爬虫/调试前清空旧日志,避免干扰分析:

# 清空所有日志缓冲区
adb logcat -c

这篇文章覆盖了ADB从入门到自动化的核心内容,足够支撑大部分App爬虫和移动端调试的需求。如果需要更复杂的界面交互(比如点击、滑动、输入),可以搭配uiautomator2/Appium等第三方库使用。