基于 2026-04-11 一加 15 (PLK110) BL 解锁备份/还原实战总结。 下次执行前通读本文档,避免踩坑。
- 列出手机存储总占用(设置 → 存储)
- 列出所有第三方 App(
adb shell pm list packages -3) - 确认录音、微信等 App 私有数据的备份方案
- 确认联系人同步状态(Google/手机云)
- 确认照片云同步状态
- 确认 2FA 恢复码已保存
- 确认 eSIM 处理方式(如有)
以下数据在 /data/data/ 下,ADB shell 无权限读取,必须在操作前单独处理:
| 数据类型 | 典型大小 | 处理方式 |
|---|---|---|
| 录音 | 1-3 GB | 手机自带备份工具(设置 → 备份与恢复)或云同步 |
| 微信聊天记录 | 5-50 GB | PC 端微信:我 → 设置 → 聊天 → 备份与迁移 |
| 银行 App 登录态 | - | 记录登录凭证,准备重新验证 |
| 2FA 密钥 | - | Google Authenticator 导出 / 记录恢复码 |
⚠️ 关键教训: 不要只列清单,必须逐项确认用户已完成备份再继续。
- USB 3.0 数据线(非仅充电线)
- PC 端 USB 3.0 接口(主板直连或显示器 USB 3.0 口)
- PC 磁盘空间 >= 手机已用空间 × 1.2
# Git Bash 下必须禁用路径转换
export MSYS_NO_PATHCONV=1
# 确认设备连接
adb devices -l# 导出第三方包名
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。
核心原则: 中文路径 + 大量文件 → 用 tar 打包,不要用 adb pull 逐文件拉。
# 注意 Git Bash 路径转换问题
adb pull //sdcard/DCIM/Camera DCIM/Camera/
adb pull //sdcard/DCIM/2024-NewYearFirework DCIM/# 在手机端打包(不压缩)
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 逐文件拉:
- 中文文件名会报 "Illegal byte sequence" 错误
- 大量小文件传输效率极低
- adb push 大目录可能 OOM 崩溃(实测 36GB/7414 文件触发
std::bad_alloc)
这是本次最大的教训。 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,不需要从文件名反推。
# 方法1: adb 导出(原始格式)
adb shell content query --uri content://com.android.contacts/contacts \
> contacts_raw.txt
# 方法2: 手机端导出 vCard(推荐)
# 设置 → 联系人 → 导出联系人 → 导出到存储
# 然后 adb pull //sdcard/Contacts.vcf .# 比对文件数
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')
"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 解包方式可以避免。
#!/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"# 首选: 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。 解决方案:
- 备份时用
pm path拉取所有 split 文件,用adb install-multiple安装- 或直接从应用商店重新安装
手机上操作:设置 → 联系人 → 导入联系人 → 从存储导入
| 问题 | 原因 | 解决方案 |
|---|---|---|
adb pull 报 "Illegal byte sequence" |
adb 对 UTF-8 中文路径处理有 bug | 手机端 tar cf 打包后拉取单个 tar |
adb push 中文路径同样失败 |
同上 | tar 打包后推送,手机端解包 |
| tar 内中文文件名 Windows 解包乱码 | toybox tar 和 Windows bsdtar 编码不同 | 用 Python tarfile 库处理(兼容性好) |
| 线缆/接口 | 实测速度 | 备注 |
|---|---|---|
| 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 数据线。
推送 36GB/7414 文件的目录时,adb 客户端内存溢出 std::bad_alloc。
解决方案:用 tar 打成单文件推送,或分批推送子目录。
Git Bash 会自动把 /sdcard/ 转为 C:/Program Files/Git/sdcard/。
解决方案:
export MSYS_NO_PATHCONV=1 # 禁用路径转换
# 或用双斜杠
adb pull //sdcard/file .su -c 'pm install /sdcard/file.apk' 失败,因为 root 进程运行在不同 SELinux context,无法读取 shell 用户推送到 /sdcard/ 的文件。解决方案:复制到 root 可访问的目录,或使用 adb install。
- 备份前确认每个"无法 ADB 备份"的项目已有方案 — 不只是列清单
- 备份阶段同步导出时间戳映射表 —
find + stat导出全部文件的 mtime - Split APK 全量拉取 —
pm path返回多行时全部下载,或标记为"需商店安装" - tar 打包优先 — 任何含中文路径或文件数 > 100 的目录都用 tar
- 备份前拍照/截图记录手机桌面布局和 Widget 配置
- 导出 WiFi 密码列表(
adb shell cat /data/misc/wifi/wpa_supplicant.conf,需 root) - 导出蓝牙配对记录
- 记录系统设置中的重要配置(开发者选项、无障碍等)
- 写一个 Python 全自动备份脚本,一键完成:盘点 → 打包 → 拉取 → 校验 → 导出时间戳
- 时间戳修复脚本改用映射表而非文件名正则匹配(覆盖率从 ~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 直推大目录既慢又不稳定。