Android 离线优先的数据同步设计
Android 离线优先的数据同步设计
离线优先并不意味着所有页面永远展示缓存,而是让界面拥有稳定的本地数据源,并在网络可用时按业务规则同步。设计不当时,常见问题包括本地修改被旧响应覆盖、离线操作重复提交、多个同步任务互相抢写,以及用户看到无法判断新旧的数据。
一、建立单一可观察数据源
页面优先观察本地数据库,网络同步只负责更新数据库。这样界面不会在接口结果和本地结果之间反复切换。
class NoteRepository(
private val noteDao: NoteDao,
private val remoteDataSource: NoteRemoteDataSource
) {
fun observeNotes(): Flow<List<Note>> {
return noteDao.observeAll()
.map { entities -> entities.map(NoteEntity::toDomain) }
}
suspend fun refresh() {
val response = remoteDataSource.queryNotes()
noteDao.replaceRemoteSnapshot(response.map(NoteDto::toEntity))
}
}
数据库事务负责确保快照替换期间不会暴露中间状态。
二、记录同步元数据
仅保存业务内容不足以处理同步。通常还需要:
- 本地修改时间。
- 服务端版本或更新时间。
- 当前同步状态。
- 最近一次失败原因。
- 稳定的操作标识。
enum class SyncState {
SYNCED,
PENDING_CREATE,
PENDING_UPDATE,
PENDING_DELETE,
FAILED
}
同步状态是数据层事实,界面可据此显示“待同步”或“失败”,而不是根据网络是否连接来猜测。
三、离线写入先落本地
用户编辑完成后,先在本地事务中保存内容并创建待同步操作,界面立即从数据库看到变化。
suspend fun saveLocalEdit(note: NoteEntity, operation: SyncOperationEntity) {
database.withTransaction {
noteDao.upsert(note)
syncOperationDao.insert(operation)
}
}
业务内容与操作记录必须共同成功。否则可能出现界面显示已保存,但没有任何任务负责同步。
四、让同步操作具备幂等性
后台任务可能重复执行。每个离线操作应有稳定标识,服务端和客户端据此识别重复提交。创建类操作尤其不能每次重试都生成新业务对象。
操作成功后,在同一事务中更新本地业务记录并删除队列项。若进程在服务端成功后、本地记录前终止,下一次重试仍应得到同一业务结果。
五、明确冲突解决规则
本地和远端同时修改同一对象时,需要字段所有权和冲突策略。常见选择包括:
- 服务端版本优先。
- 本地未同步编辑优先。
- 最后修改时间优先。
- 不同字段分别合并。
- 标记冲突并让用户选择。
时间优先只有在双方时钟和时间语义可靠时才成立。金融确认、审批等关键数据不应仅凭客户端时间覆盖。
六、删除也要建模
离线删除后不能立即丢掉所有同步线索。可以保留删除标记或待删除操作,等服务端确认后再彻底清理。
如果同步刷新时服务端仍返回该对象,本地删除状态不应被直接覆盖。删除由谁拥有最终解释权,必须在协议中明确。
七、网络恢复不等于立即同步全部
网络状态只说明当前可能具备通信条件,不保证服务可用。同步任务仍应处理超时、身份失效和业务拒绝。
大量积压操作恢复时要控制并发和顺序。同一对象的创建、更新、删除必须按依赖执行,不同对象是否并发则根据服务能力决定。
持久后台任务应交给项目已有的任务调度组件,不能只依赖页面协程。
八、向用户表达数据状态
离线优先不能掩盖数据时效性。页面可以显示:
- 最近同步时间。
- 当前离线状态。
- 某条记录正在同步或同步失败。
- 需要用户处理的冲突。
旧数据如果可能导致错误决策,就不应以正常样式无提示展示。是否允许继续操作也要按业务风险控制。
九、限制队列和存储增长
连续编辑同一草稿时,可以在保证语义一致的前提下合并多条待更新操作。创建后立即删除且从未同步的对象,可能直接取消两项操作。
不能合并有审计意义的动作,例如逐笔交易指令。队列压缩规则必须与业务语义一致。
十、测试异常恢复
重点覆盖:
- 无网络时编辑是否成功落本地。
- 进程重启后待同步操作是否仍存在。
- 同一操作重复执行是否产生重复数据。
- 服务端成功、本地提交前中断后能否恢复。
- 本地编辑与远端更新冲突是否按规则处理。
- 删除标记是否会被远端旧快照覆盖。
- 账户退出时队列是否按范围处理。
总结
离线优先架构的基础是本地单一数据源和持久操作队列。通过同步元数据记录状态,以幂等操作支持重试,再明确更新、删除和冲突的最终解释权,应用才能在断网、进程重建和多端修改下保持数据可信。
- 点赞
- 收藏
- 关注作者
评论(0)