Skip to content

Instantly share code, notes, and snippets.

@felix021
Created April 11, 2026 15:00
Show Gist options
  • Select an option

  • Save felix021/43f43ef283a3e7fca7dc24d388d8b984 to your computer and use it in GitHub Desktop.

Select an option

Save felix021/43f43ef283a3e7fca7dc24d388d8b984 to your computer and use it in GitHub Desktop.
Android 手机 ADB 备份/还原 SOP

Android 手机 ADB 备份/还原 SOP

基于 2026-04-11 一加 15 (PLK110) BL 解锁备份/还原实战总结。 下次执行前通读本文档,避免踩坑。


一、执行前 Checklist(必须在操作前逐项确认)

1.1 数据盘点

  • 列出手机存储总占用(设置 → 存储)
  • 列出所有第三方 App(adb shell pm list packages -3
  • 确认录音、微信等 App 私有数据的备份方案
  • 确认联系人同步状态(Google/手机云)
  • 确认照片云同步状态
  • 确认 2FA 恢复码已保存
  • 确认 eSIM 处理方式(如有)

1.2 App 私有数据(ADB 无法访问)

以下数据在 /data/data/ 下,ADB shell 无权限读取,必须在操作前单独处理

数据类型 典型大小 处理方式
录音 1-3 GB 手机自带备份工具(设置 → 备份与恢复)或云同步
微信聊天记录 5-50 GB PC 端微信:我 → 设置 → 聊天 → 备份与迁移
银行 App 登录态 - 记录登录凭证,准备重新验证
2FA 密钥 - Google Authenticator 导出 / 记录恢复码

⚠️ 关键教训: 不要只列清单,必须逐项确认用户已完成备份再继续。

1.3 硬件确认

  • USB 3.0 数据线(非仅充电线)
  • PC 端 USB 3.0 接口(主板直连或显示器 USB 3.0 口)
  • PC 磁盘空间 >= 手机已用空间 × 1.2

二、备份流程

2.1 环境准备

# Git Bash 下必须禁用路径转换
export MSYS_NO_PATHCONV=1

# 确认设备连接
adb devices -l

2.2 导出 App 列表和 APK

# 导出第三方包名
adb shell pm list packages -3 | sed 's/package://' > package_list.txt

# 导出每个 APK 的路径
for pkg in $(cat package_list.txt); do
  paths=$(adb shell pm path "$pkg" 2>/dev/null | sed 's/package://')
  echo "$pkg|$paths" >> apk_paths.txt
done

# 逐个拉取 APK
mkdir -p apks
while IFS='|' read -r pkg paths; do
  for p in $paths; do
    base=$(basename "$p")
    # 注意:Split APK 有多个文件,都要拉
    adb pull "$p" "apks/${pkg}.apk"
  done
done < apk_paths.txt

⚠️ Split APK 陷阱: pm path 对 App Bundle 只返回 base.apk,缺少 split APK 会导致安装失败。 正确做法:pm path 返回多行时,每个都要拉取并记录。或者用 adb install-multiple 安装。 已知 Split APK 常见应用:Chrome, Google Maps, YouTube, Gmail, WhatsApp, Telegram, VLC 等系统/商店分发的大厂 App。

2.3 备份媒体文件

核心原则: 中文路径 + 大量文件 → 用 tar 打包,不要用 adb pull 逐文件拉。

直拉目录(少量文件/全英文路径)

# 注意 Git Bash 路径转换问题
adb pull //sdcard/DCIM/Camera DCIM/Camera/
adb pull //sdcard/DCIM/2024-NewYearFirework DCIM/

大目录/含中文路径(用 tar 打包)

# 在手机端打包(不压缩)
adb shell "tar cf /sdcard/Pictures.tar -C /sdcard Pictures"
adb pull //sdcard/Pictures.tar .

# 对每个大目录重复
# tar cf /sdcard/<name>.tar -C /sdcard <dir>

为什么不用 adb pull 逐文件拉:

  1. 中文文件名会报 "Illegal byte sequence" 错误
  2. 大量小文件传输效率极低
  3. adb push 大目录可能 OOM 崩溃(实测 36GB/7414 文件触发 std::bad_alloc

2.4 ⭐ 备份时同步导出时间戳映射表(关键步骤)

这是本次最大的教训。 adb pull/push 会重置文件 mtime,导致相册排序混乱。 正确做法是备份阶段就记录,而不是还原后再补救。

# 方法1: 手机端导出所有文件的 mtime(推荐)
adb shell "find /sdcard/DCIM /sdcard/Pictures /sdcard/Music /sdcard/Movies \
  /sdcard/Download /sdcard/Documents -type f -exec stat -c '%Y %n' {} \; \
  > /sdcard/timestamp_map.txt"
adb pull //sdcard/timestamp_map.txt .

# 方法2: 如果 find+stat 不支持,用 toybox ls
adb shell "ls -laR /sdcard/DCIM/ > /sdcard/dcim_listing.txt"
adb pull //sdcard/dcim_listing.txt .

时间戳映射表格式:unix_timestamp 完整路径

还原时直接用映射表恢复 mtime,不需要从文件名反推。

2.5 备份联系人

# 方法1: adb 导出(原始格式)
adb shell content query --uri content://com.android.contacts/contacts \
  > contacts_raw.txt

# 方法2: 手机端导出 vCard(推荐)
# 设置 → 联系人 → 导出联系人 → 导出到存储
# 然后 adb pull //sdcard/Contacts.vcf .

2.6 校验

# 比对文件数
adb shell "find /sdcard/DCIM/Camera -type f | wc -l"
find DCIM/Camera -type f | wc -l

# 比对目录大小
adb shell "du -sh /sdcard/DCIM/Camera"
du -sh DCIM/Camera

# tar 文件校验
python3 -c "
import tarfile
with tarfile.open('Pictures.tar', 'r') as t:
    members = t.getmembers()
    print(f'Entries: {len(members)}')
    total = sum(m.size for m in members if m.isfile())
    print(f'Total size: {total/1024/1024/1024:.2f} GB')
"

三、还原流程

3.1 恢复媒体文件

export MSYS_NO_PATHCONV=1

# 方式A: tar 推到手机端解包(推荐,快)
adb push Pictures.tar //sdcard/
adb shell "tar xf /sdcard/Pictures.tar -C /sdcard/ && rm /sdcard/Pictures.tar"

# 方式B: PC 解包后 push(可校验,但 push 大目录可能 OOM)
# 大目录不要用此方式!

路径嵌套陷阱: adb push D:/backup/DCIM/Camera /sdcard/DCIM/ 会创建 /sdcard/DCIM/Camera/Camera/。 用 tar 解包方式可以避免。

3.2 恢复时间戳(使用备份阶段导出的映射表)

#!/usr/bin/env python3
"""从时间戳映射表生成 touch 命令"""
import sys
from datetime import datetime

with open("timestamp_map.txt") as f:
    for line in f:
        parts = line.strip().split(" ", 1)
        if len(parts) != 2:
            continue
        ts, path = parts
        try:
            dt = datetime.fromtimestamp(int(ts))
            touch = dt.strftime("%Y%m%d%H%M.%S")
            print(f'touch -t {touch} "{path}"')
        except:
            pass
# 生成脚本
python3 gen_touch.py > fix_ts.sh

# 推送到手机执行
adb push fix_ts.sh //sdcard/
adb shell "sh /sdcard/fix_ts.sh"

3.3 安装 APK

# 首选: pm install 流式安装(绕过 UI 确认)
# 注意:需要 root,且 root 的 SELinux context 可能阻止读取 shell 用户文件
# 实测 ColorOS 16 + Magisk 不行

# 次选: adb install + UI 自动化
# 用 uiautomator 抓取 resource-id(不是坐标!)
adb shell "uiautomator dump /sdcard/ui.xml"
# 解析 XML 找到 resource-id,然后用 click 命令

# 最稳: 手动批量安装
for apk in apks/*.apk; do
  adb install "$apk"
  # 手机上点"继续安装"
done

⚠️ 不要用坐标点击: 不同 ROM 版本、不同屏幕分辨率下按钮位置不同。 如果必须自动化,用 resource-id 定位。

⚠️ Split APK: App Bundle 格式的应用,单个 base.apk 安装会报 INSTALL_FAILED_MISSING_SPLIT。 解决方案:

  1. 备份时用 pm path 拉取所有 split 文件,用 adb install-multiple 安装
  2. 或直接从应用商店重新安装

3.4 恢复联系人

手机上操作:设置 → 联系人 → 导入联系人 → 从存储导入


四、踩坑记录

4.1 中文路径问题

问题 原因 解决方案
adb pull 报 "Illegal byte sequence" adb 对 UTF-8 中文路径处理有 bug 手机端 tar cf 打包后拉取单个 tar
adb push 中文路径同样失败 同上 tar 打包后推送,手机端解包
tar 内中文文件名 Windows 解包乱码 toybox tar 和 Windows bsdtar 编码不同 用 Python tarfile 库处理(兼容性好)

4.2 USB 传输速度

线缆/接口 实测速度 备注
USB 2.0 充电线 + USB 2.0 HUB ~40 MB/s 初始配置
Type-C 充电线 + 主板 USB 口 ~80 MB/s 中等
USB 3.0 数据线 + 显示器 USB 3.0 口 ~144 MB/s 最优

关键: 充电线可能只支持 USB 2.0。确认使用支持数据传输的 USB 3.0 数据线。

4.3 adb push 大目录 OOM

推送 36GB/7414 文件的目录时,adb 客户端内存溢出 std::bad_alloc。 解决方案:用 tar 打成单文件推送,或分批推送子目录。

4.4 Git Bash MSYS 路径转换

Git Bash 会自动把 /sdcard/ 转为 C:/Program Files/Git/sdcard/。 解决方案:

export MSYS_NO_PATHCONV=1   # 禁用路径转换
# 或用双斜杠
adb pull //sdcard/file .

4.5 Root 权限与 SELinux

su -c 'pm install /sdcard/file.apk' 失败,因为 root 进程运行在不同 SELinux context,无法读取 shell 用户推送到 /sdcard/ 的文件。解决方案:复制到 root 可访问的目录,或使用 adb install


五、下次改进清单

必须做

  1. 备份前确认每个"无法 ADB 备份"的项目已有方案 — 不只是列清单
  2. 备份阶段同步导出时间戳映射表find + stat 导出全部文件的 mtime
  3. Split APK 全量拉取pm path 返回多行时全部下载,或标记为"需商店安装"
  4. tar 打包优先 — 任何含中文路径或文件数 > 100 的目录都用 tar

建议做

  1. 备份前拍照/截图记录手机桌面布局和 Widget 配置
  2. 导出 WiFi 密码列表(adb shell cat /data/misc/wifi/wpa_supplicant.conf,需 root)
  3. 导出蓝牙配对记录
  4. 记录系统设置中的重要配置(开发者选项、无障碍等)

工具改进

  1. 写一个 Python 全自动备份脚本,一键完成:盘点 → 打包 → 拉取 → 校验 → 导出时间戳
  2. 时间戳修复脚本改用映射表而非文件名正则匹配(覆盖率从 ~70% 提升到 ~100%)

六、速度参考

基于一加 15 + Windows 10 + USB 3.0 的实测数据:

操作 数据量 耗时 速度
adb pull 单文件 (tar) 7.42 GB ~1 min ~144 MB/s
adb pull 大目录 (7414 files) 36.37 GB ~20 min ~30 MB/s (开销大)
adb push 单文件 (tar) 7 GB ~1 min ~140 MB/s
adb push 大目录 36 GB OOM 崩溃 不可用
手机端 tar cf 7 GB ~2 min ~60 MB/s
手机端 tar xf 7 GB ~2 min ~60 MB/s

结论: 大目录务必用 tar 打包传输,adb push/pull 直推大目录既慢又不稳定。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment