
版本和问题范围这篇按 HarmonyOS 5.0.0 及以上的应用质量和上架前自查场景来写。开发环境按 DevEco Studio 6.0 Release、ArkTS、Stage 模型来组织示例。这里讨论的不是云同步也不是账号体系而是应用本地数据在换机、恢复、重新安装后的可用性问题。备份恢复很容易被写成“把目录打包”。但真放到应用里这个思路会出问题用户真正需要保留的是手写内容、偏好设置、关键记录图片缓存、搜索索引、临时统计这类数据可以重建强行带走反而可能把旧状态和新版本代码绑死。先把数据分成三类类型例子是否建议备份原因用户主动产生的数据收藏、笔记、内容记录、已买清单建议丢了就不可恢复用户偏好主题、排序方式、最近使用入口视情况影响体验但要能兜底可重建数据图片缓存、搜索索引、统计缓存不建议体积大恢复后也可能过期我现在写备份恢复会先建一个白名单而不是扫目录。白名单能逼着自己回答一个问题这个字段丢了用户是不是会真的受影响如果只是为了让页面快一点生成的缓存就别让它进入备份包。Case A收藏和笔记要带版本号收藏、笔记、内容记录这类数据属于用户主动产生的数据必须优先保留。但保留不等于原样塞回去。应用版本升级后字段结构可能变了所以备份包里要有 schemaVersion。interface BackupMeta { schemaVersion: number appVersion: string createdAt: number } interface FavoriteBackupItem { id: string recipeId: string title: string note?: string updatedAt: number } interface AppBackupPayload { meta: BackupMeta favorites: FavoriteBackupItem[] } const CURRENT_BACKUP_SCHEMA 3 function buildBackupPayload(favorites: FavoriteBackupItem[]): AppBackupPayload { return { meta: { schemaVersion: CURRENT_BACKUP_SCHEMA, appVersion: 1.6.0, createdAt: Date.now() }, favorites: favorites.map(item ({ id: item.id, recipeId: item.recipeId, title: item.title, note: item.note ?? , updatedAt: item.updatedAt })) } }这个结构看起来多写了几行但恢复时会省很多事。没有版本号的备份包后面只能靠猜字段有版本号就可以明确走迁移函数。Case B搜索索引和图片缓存不要跟着走另一类数据很容易被误放进备份搜索索引、缩略图缓存、统计缓存。这些东西不是用户资产是运行时优化结果。恢复它们的风险有三个体积大、容易过期、可能和新版本算法不一致。更稳的做法是恢复用户数据后重建缓存。interface RestoreResult { restoredFavorites: number rebuiltSearchIndex: boolean ignoredCacheFiles: string[] } function shouldBackup(path: string): boolean { const allowList [/data/favorites.json, /data/notes.json, /settings/user-preference.json] return allowList.includes(path) } async function restoreAfterInstall(payload: AppBackupPayload): PromiseRestoreResult { const migrated migrateBackup(payload) await saveFavoritesToRdb(migrated.favorites) // 恢复后重建不把旧缓存当成可信数据。 await rebuildSearchIndex(migrated.favorites) await clearImageCacheIfVersionChanged() return { restoredFavorites: migrated.favorites.length, rebuiltSearchIndex: true, ignoredCacheFiles: [/cache/images, /cache/search-index] } }这里的关键是恢复顺序先迁移用户数据再入库再重建索引最后清理不可信缓存。不要一上来把缓存目录恢复回去否则新版本第一次启动时很容易出现“数据是新的索引还是旧的”。旧版本备份怎么迁移如果备份包来自旧版本字段可能不完整。比如早期收藏只有 recipeId后来才加 title、note、updatedAt。恢复时不能因为缺字段就崩也不能随便写空对象。function migrateBackup(payload: AppBackupPayload): AppBackupPayload { if (payload.meta.schemaVersion CURRENT_BACKUP_SCHEMA) { return payload } if (payload.meta.schemaVersion 1) { return { meta: { ...payload.meta, schemaVersion: CURRENT_BACKUP_SCHEMA }, favorites: payload.favorites.map(item ({ id: item.id || item.recipeId, recipeId: item.recipeId, title: item.title || 未命名菜谱, note: item.note ?? , updatedAt: item.updatedAt || Date.now() })) } } if (payload.meta.schemaVersion 2) { return { meta: { ...payload.meta, schemaVersion: CURRENT_BACKUP_SCHEMA }, favorites: payload.favorites.map(item ({ ...item, note: item.note ?? })) } } throw new Error(unsupported backup schema: payload.meta.schemaVersion) }我不会让恢复流程直接吞掉异常。无法识别的版本要明确失败并给出可读提示能迁移的版本则要走固定迁移逻辑。这样比“try catch 后继续启动”更安全。恢复后要做三项校验第一项是数量校验备份包里有多少条收藏恢复后 RDB 里应该有多少条。第二项是字段校验关键字段不能空缺失字段要按迁移规则补默认值。第三项是缓存校验搜索索引和图片缓存应该是重建出来的不应该直接来自旧备份。async function verifyRestore(payload: AppBackupPayload): Promisevoid { const dbCount await countFavoritesFromRdb() if (dbCount ! payload.favorites.length) { throw new Error(restore count mismatch: db${dbCount}, backup${payload.favorites.length}) } const invalid await findInvalidFavoriteRows() if (invalid.length 0) { throw new Error(restore invalid rows: invalid.length) } const indexReady await checkSearchIndexReady() if (!indexReady) { await rebuildSearchIndex(payload.favorites) } }校验不是为了写得复杂而是为了避免恢复后页面能打开但里面悄悄少数据。备份恢复属于用户强感知能力宁愿恢复慢一点也不能恢复错。本地小验证我用一个小脚本验证迁移逻辑旧版本备份只有 recipeId新版本恢复后必须补 title、note、updatedAt并且缓存路径不会进入备份白名单。const oldPayload { meta: { schemaVersion: 1, appVersion: 1.0.0, createdAt: 1000 }, favorites: [{ id: , recipeId: recipe_001, title: , updatedAt: 0 }] } const restored migrateBackup(oldPayload as AppBackupPayload) console.assert(restored.meta.schemaVersion 3) console.assert(restored.favorites[0].title 未命名菜谱) console.assert(shouldBackup(/cache/images) false) console.assert(shouldBackup(/data/favorites.json) true)这类验证最好写在迁移函数旁边。以后字段变化时先补迁移用例再改恢复逻辑。不要等用户换机后才发现旧备份恢复不了。最后总结HarmonyOS 应用做备份恢复重点不是“能不能把文件拿回来”而是恢复后的数据能不能被当前版本稳定读取。我的判断规则很简单用户主动产生的数据优先保留可重建的缓存不要进入备份恢复后必须迁移、入库、重建索引、再校验。这么做的好处是上架前自查更容易闭环后续版本升级也不会被旧缓存拖住。对用户来说真正重要的是收藏、笔记、内容记录还在对开发来说真正重要的是恢复后的数据结构仍然可信。