17、预条件检查与数据备份:电池电量检查、存储空间检查、用户数据备份策略

系统升级这事儿,最怕什么?

怕升级到一半,手机没电了。怕存储空间不够,解压到一半卡死了。更怕用户数据丢了,回头来找你拼命。

所以,在真正开始升级之前,我们得做一套「预条件检查」。说白了,就是给系统做个全面体检。体检不合格,坚决不让升级流程往下走。

我个人习惯,把这部分代码叫做 PreconditionsChecker。它就像个守门员,把各种风险挡在门外。

17.1 电池电量检查:别让升级变成「砖」

为什么电量检查是第一道关?

你想想看,升级过程中如果突然断电,系统可能处于一个「半升级」状态。bootloader 刷了一半,或者 system 分区写了一半。下次开机,大概率变砖。

我在项目中遇到过,有用户插着充电线升级,但充电线接触不良。电量从 60% 一路掉到 5%,升级还没完成,手机直接黑屏了。嗯,从那以后,我坚持要做「实时电量监控」。

核心策略:
  • 最低阈值:我一般设 30%。低于这个值,直接弹窗提示,拒绝升级。
  • 安全阈值:30%~50%,允许升级,但会提示「建议充电」。同时后台持续监控电量变化。
  • 理想阈值:50% 以上,直接放行。

代码实现上,我们通过 BatteryManager 获取电量。注意,这里有个坑:

// 获取电量百分比
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level * 100 / (float) scale;

// 检查是否在充电
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
                  || status == BatteryManager.BATTERY_STATUS_FULL;
注意:不要只检查一次电量。我建议在升级过程中,每隔 30 秒重新读取一次电量。如果发现电量下降过快(比如每分钟掉 2% 以上),应该暂停升级并提示用户。

17.2 存储空间检查:给升级包留足「余粮」

升级包本身要占空间,解压出来的文件也要占空间,系统备份还要占空间。这三者加起来,才是你真正需要的空间。

我见过一个案例:升级包 1.5GB,用户手机还剩 2GB 空间。看起来够了吧?结果解压时需要临时文件,加上系统备份,直接撑爆了。升级失败,系统回滚,用户数据差点丢了。

所以,我的计算方式是:

项目 所需空间 说明
升级包本身 包大小 × 1.2 留 20% 余量,防止碎片化
解压临时空间 包大小 × 2 OTA 包通常是压缩的,解压后更大
系统备份空间 系统分区大小 × 0.3 备份关键分区(boot、system)
用户数据备份 用户数据大小 × 0.5 视备份策略而定

代码里,我习惯这样检查:

// 获取 data 分区可用空间
StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
long availableBytes = stat.getAvailableBlocksLong() * stat.getBlockSizeLong();

// 计算所需空间
long requiredBytes = otaPackageSize * 3 + systemPartitionSize / 3 + userDataSize / 2;

if (availableBytes < requiredBytes) {
    // 提示用户清理空间
    showStorageWarning(requiredBytes - availableBytes);
    return false;
}
我的经验:检查存储空间时,别忘了检查 /cache 分区。很多 OTA 升级会把临时文件放在 cache 分区。如果 cache 分区满了,升级也会失败。

17.3 用户数据备份策略:给数据上「保险」

升级过程中,最怕的就是数据丢失。虽然现在的 OTA 升级很少会擦除用户数据,但万一呢?

我个人的原则是:宁可备而不用,不可用而不备。

备份策略分三种级别:

17.3.1 轻量级备份:只备份关键配置

适合日常小版本升级。备份内容:

  • WiFi 密码、蓝牙配对信息
  • 系统设置(亮度、音量、壁纸等)
  • 应用列表(不备份应用数据)
// 备份系统设置到 /data/backup/settings_backup.xml
private void backupSystemSettings() {
    // 读取 Settings.System、Settings.Secure、Settings.Global
    // 序列化为 XML 文件
    // 存储到 /data/backup/ 目录
}

17.3.2 中等备份:备份应用数据

适合跨版本升级(比如 Android 12 升到 13)。备份内容:

  • 所有用户安装的应用及其数据
  • 联系人、短信、通话记录
  • 相册、下载文件
注意:应用数据备份需要应用本身支持。如果应用没有声明 android:allowBackup="true",系统不会备份它的数据。我在项目中就踩过这个坑,升级后用户发现某个应用的数据全丢了,后来才发现是应用没开备份权限。

17.3.3 全量备份:整机镜像

适合重大升级或用户主动要求。备份内容:

  • 整个 /data 分区
  • 使用 dd 命令或 fs_mgr 工具
  • 备份到外置存储或 OTG 设备
// 全量备份 data 分区(需要 root 权限)
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("dd if=/dev/block/by-name/userdata of=/sdcard/userdata_backup.img bs=4M\n");
os.writeBytes("exit\n");
os.flush();
警告:全量备份非常耗时,而且占用空间巨大。我建议只在「设置」中提供一个入口,让用户手动触发,不要自动执行。否则用户会抱怨「升级怎么要等半小时」。

17.4 预条件检查的完整流程

说了这么多,我们把这些检查串起来。下面是我常用的流程图:

预条件检查流程图 开始升级请求 电池电量检查 是否 ≥ 30% 或正在充电? 存储空间检查 可用空间 ≥ 所需空间? 用户数据备份 根据升级类型选择备份级别 通过,开始升级 失败:提示用户

这个流程看起来简单,但实际落地时,每个环节都有不少细节。我建议你把检查结果都记录到日志里,方便排查问题。

一个小技巧:预条件检查失败时,不要只弹一个「升级失败」的提示。要告诉用户具体原因,比如「电量不足,请充电后再试」或「存储空间不足,请清理 500MB 空间」。用户知道该怎么做,体验会好很多。

好了,预条件检查和数据备份就聊到这儿。这些代码写起来不难,但考虑周全了,能帮你省掉很多售后麻烦。


公众号:蓝海资料掘金营,微信deep3321