ADB 工具函数 Python 使用教程

环境准备

  1. 安装 Platform-Tools(官网下载) 并将其路径加入系统环境变量 PATH。
  2. 手机开启「开发者选项」中的「USB 调试」功能。

执行命令验证设备连接:

bash 复制代码
adb devices

输出中显示设备状态为 device 表示已授权成功。记录设备序列号,例如:3B158E00A3V00000。

核心函数:adb()

函数签名含义
python 复制代码
def adb(cmd: str, check: bool = True) -> str
  • cmd: 要执行的 ADB 命令字符串,如 'shell ls /sdcard'、'pull a.jpg D:/a.jpg'。
  • check: 是否检查返回码。若为 True 且命令失败,抛出异常;若为 False,返回空字符串,脚本继续运行。
推荐实现
python 复制代码
import subprocess
from pathlib import Path

DEVICE_SERIAL = "3B158E00A3V00000"  # 替换为你的设备序列号

def adb(cmd: str, check: bool = True) -> str:
    """执行 adb 命令。支持 shell、pull、push 等操作"""
    full_cmd = f"adb -s {DEVICE_SERIAL} {cmd}"
    result = subprocess.run(
        full_cmd,
        shell=True,
        capture_output=True,
        text=True,
        encoding="utf-8",
        errors="replace",
    )
    if check and result.returncode != 0:
        raise RuntimeError(f"ADB 失败: {full_cmd}\n{result.stderr}")
    return result.stdout.strip()
调用方式
python 复制代码
# 检查错误(默认行为),失败则抛异常
brand = adb("shell getprop ro.product.brand")

# 允许失败,不中断脚本,返回空串
raw = adb("shell ls /sdcard/Pictures", check=False)

# 截图并拉取
adb("shell screencap -p /sdcard/screen.png")
adb(r'pull /sdcard/screen.png D:\screen.png')

常用工具函数

推送图片(固定文件名,避免相册乱序)
python 复制代码
def push_image_to_phone(local_path: Path) -> str:
    # 步骤1:确保目录存在(不删除目录,保留 MediaStore 已有路径记录)
    adb(f"shell mkdir -p {PHONE_TMP_DIR}")

    # 步骤2:以固定文件名覆盖推送
    phone_path = f"{PHONE_TMP_DIR}/{PHONE_FIXED_FILENAME}"
    print(f"  [ADB PUSH] {local_path.name} → {phone_path}")
    adb(f'push "{local_path}" {phone_path}')

    # 步骤3:通知 MediaStore 重新扫描该路径,更新缩略图和索引
    adb(
        f"shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE "
        f"-d file://{phone_path}",
        check=False
    )
    return phone_path

拉取单个文件或目录
python 复制代码
def pull_single_file(phone_path: str, local_dir: Path, rename: str = "") -> Path:
    local_dir.mkdir(parents=True, exist_ok=True)
    fname = rename or Path(phone_path).name
    local_dst = local_dir / fname
    adb(f'pull "{phone_path}" "{local_dst}"')
    return local_dst

获取手机目录下所有文件名(集合形式)
python 复制代码
def get_phone_dir_files(directory: str) -> set[str]:
    raw = adb(f"shell ls {directory}", check=False)
    if not raw:
        return set()
    return {line.strip() for line in raw.splitlines() if line.strip()}

差量拉取新生成的图片
python 复制代码
PHONE_DOWNLOAD_DIR = "/sdcard/Pictures"

def pull_new_images(before: set[str], output_dir: Path, prefix: str) -> list[Path]:
    after = get_phone_dir_files(PHONE_DOWNLOAD_DIR)
    new_files = after - before
    saved = []
    for fname in sorted(new_files):
        dst = output_dir / f"{prefix}_{fname}"
        adb(f'pull "{PHONE_DOWNLOAD_DIR}/{fname}" "{dst}"')
        saved.append(dst)
    return saved
用法示例
python 复制代码
before = get_phone_dir_files(PHONE_DOWNLOAD_DIR)
# ... 触发 App 生成内容 ...
saved = pull_new_images(before, Path(r"D:\output"), prefix="case001")
print(f"已保存 {len(saved)} 张新图片")

Shell / 按键速用

python 复制代码
adb("shell getprop ro.product.model")      # 获取设备型号
adb("shell input keyevent 4")              # 按返回键
adb("shell input keyevent 3")              # 按 Home 键
adb("shell input tap 540 960")             # 点击屏幕坐标 (540, 960)
adb("shell am force-stop com.larus.nova")   # 强制停止指定应用

最小可运行示例

python 复制代码
# demo_adb.py
from pathlib import Path

DEVICE_SERIAL = "3B158E00A3V00000"  # 替换为你的设备序列号

# 粘贴以下函数定义:
def adb(cmd: str, check: bool = True) -> str:
    full_cmd = f"adb -s {DEVICE_SERIAL} {cmd}"
    result = subprocess.run(
        full_cmd,
        shell=True,
        capture_output=True,
        text=True,
        encoding="utf-8",
        errors="replace",
    )
    if check and result.returncode != 0:
        raise RuntimeError(f"ADB 失败: {full_cmd}\n{result.stderr}")
    return result.stdout.strip()

PHONE_TMP_DIR = "/sdcard/doubao_tmp"
PHONE_FIXED_FILENAME = "doubao_upload.jpg"

def push_image_to_phone(local_path: Path) -> str:
    adb(f"shell mkdir -p {PHONE_TMP_DIR}")
    phone_path = f"{PHONE_TMP_DIR}/{PHONE_FIXED_FILENAME}"
    adb(f'push "{local_path}" {phone_path}')
    adb(
        f"shell am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE "
        f"-d file://{phone_path}",
        check=False,
    )
    return phone_path

def get_phone_dir_files(directory: str) -> set[str]:
    raw = adb(f"shell ls {directory}", check=False)
    if not raw:
        return set()
    return {line.strip() for line in raw.splitlines() if line.strip()}

if __name__ == "__main__":
    print("brand =", adb("shell getprop ro.product.brand"))
    print("model =", adb("shell getprop ro.product.model"))
    files = get_phone_dir_files("/sdcard/Pictures")
    print("Pictures count =", len(files))

运行前确保设备已连接且授权。


常见问题

问题 解决方案
adb: command not found 检查 Platform-Tools 是否正确安装并加入 PATH
error: device not found USB 连接不稳定,尝试重新插拔或重启 ADB (adb kill-server, adb start-server)
图片未出现在相册 使用 am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE 手动触发扫描
中文输入无效 改用 uiautomator2 的 set_text(),避免直接调用 input text
文件拉取失败(权限/路径) 确保路径存在,使用 mkdir -p 创建目录,避免路径拼写错误

设计要点(写脚本时记住)

  • 所有 ADB 调用必须通过封装函数 adb() 统一入口,禁止直接拼写 adb -s xxx。
  • 可能失败的操作(如 ls 空目录、广播扫描)使用 check=False 防止脚本中断。
  • 推送图片使用固定路径覆盖,避免因文件名随机导致媒体库污染。
  • 拉取结果采用差量集合比对,防止误拉历史文件,保证数据准确性。
  • 路径处理统一使用 Path,避免字符串拼接错误。
相关推荐
小小张说故事2 小时前
Python 里 is 和 == 到底差在哪?拆开小整数池、字符串驻留与 0ns 身份比较
python
滴啦嘟啦哒2 小时前
【具身笔记】【Piper+PI0.5】三、探究失败原因
笔记·python·vla·具身
滕州市燕猫虎计算机科技工作室个体工商户2 小时前
Java,Java NIO,Java NIO2,Java AIO,AsynchronousServerSocketChannel
java·开发语言·nio·aio·nio2
古城小栈2 小时前
Rust 学习路线,供初学者参考
开发语言·rust
喵了几个咪2 小时前
GoWind CMS 拆解:给 NL2SQL 上四把锁——智能问数的安全边界
开发语言·后端·安全·微服务·golang·gowind
多弗朗皮卡丘2 小时前
C++的类型转换
开发语言·c++
W_326002 小时前
Python 图像处理第二十八章|OpenCV 形态学操作进阶:腐蚀、膨胀、开运算、闭运算、梯度、顶帽黑帽
图像处理·人工智能·python·opencv·计算机视觉
BD_Marathon2 小时前
【Python】标准多态和鸭子多态
python
用户8356290780512 小时前
Python 操作 Excel:创建数组公式与 R1C1 公式
后端·python