Android 离线同步:补偿队列与脏数据回收怎么收口
## Android 离线同步问题
Android 离线同步最难的不是把请求发出去,而是断网、重试、恢复网络之后,怎么确认本地状态、远端结果和补偿队列三边没有错位。只要其中一边慢一步,用户看到的就不是‘同步稍后完成’,而是旧数据回写、重复提交或者列表状态来回跳。这个问题更像数据账本收口,不是平台升级或 ROM 适配那一路。
## 方案
最稳的做法是单独建一张 pending queue,把本地写入、待同步状态、最后尝试时间和失败原因全部挂进去。然后把状态拆成 queued、acked、applied 三段,只有真正走到 applied,Android 离线同步才算闭环。这样一来,问题不会永远堆成一句“同步偶发失败”,而是能明确落到队列、回写确认或脏数据回收其中一段。
## 示例代码
1. 待同步队列表
@Entity(tableName = "pending_sync")
data class PendingSyncEntity(
@PrimaryKey val id: String,
val payload: String,
val state: String,
val updatedAt: Long
)
2. Worker 执行入口
class SyncWorker(
appContext: Context,
params: WorkerParameters
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
return Result.retry()
}
}
3. 队列观测命令
adb shell dumpsys jobscheduler | findstr SyncWorker
adb shell am broadcast -a androidx.work.diagnostics.REQUEST_DIAGNOSTICS -p com.example.app
## 注意点
Android 离线同步最危险的不是一次失败,而是同一份 pending 队列同时装着“可自动重试”“需要人工合并”“已经过期可丢弃”三类数据。只要这三类不分桶,后面每次回查都会越查越乱。真正该做的是先把脏数据按风险分桶,再决定自动修还是人工介入。
## 报错与排查
1. 队列一直不出队
fun shouldRetrySync(retryCount: Int, hasNetwork: Boolean): Boolean {
return hasNetwork && retryCount < 5
}
2. 服务端已成功但本地未确认
data class SyncAck(
val localId: String,
val remoteVersion: Long,
val applied: Boolean
)
## 可运行片段
1. 最小状态机
enum class SyncState {
QUEUED, ACKED, APPLIED, FAILED
}
2. 最小演示入口
fun runOfflineSyncDemo() {
val state = SyncState.QUEUED
println(state)
}
3. 回归命令
adb shell dumpsys jobscheduler | findstr SyncWorker
adb logcat -d | findstr /I "retry pending conflict ack"
sqlite3 app.db "select id,state,updatedAt from pending_sync order by updatedAt desc limit 20;"
补一句最关键的回归要求:不要只确认 Worker 重新跑了,还要确认 pending 队列确实减少、ack 状态被写回、本地列表已经切到最新版本。否则 Android 离线同步 看起来恢复了,实际上只是把旧问题延后。
