环境准备
- 安装 Platform-Tools(官网下载) 并将其路径加入系统环境变量
PATH。 - 手机开启「开发者选项」中的「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,避免字符串拼接错误。