Android 同步队列治理实录:弱网补偿、脏数据回收与本地回写怎么拆成三段验收

作者: Android学习网 分类: Android网络编程 发布时间: 2026-04-02 18:05

## Android 数据同步治理问题

很多同步链路不是死在请求发不出去,而是死在发出去了以后没人能解释“哪一段已经成功、哪一段只是看起来成功”。我这次盯的不是常见的重试配置,而是 Android 数据同步治理 在弱网补偿、脏数据回收和本地回写这三段之间到底谁负责记账、谁负责补证据、谁负责最终收口。如果现场日志只有成功/失败两个值,后面一旦出现脏数据残留、列表闪回或者已提交状态被旧数据覆盖,团队就只能靠猜。

## Android 数据同步治理先这样修

我的处理顺序不是先改 Worker,而是先把验收口径定死。第一步,给每条待同步记录补 `localVersion / remoteVersion / writeBackState` 三组字段,先保证事后能审;第二步,把补偿任务和脏数据回收任务拆成两个独立入口,避免一个任务里同时承担提交、比对、修脏三件事;第三步,再把 UI 只消费“已验收完成”的状态,而不是消费任意一条刚落库的中间态。

## Android 数据同步治理示例代码

下面这组片段不再沿用旧稿那套“幂等键 + 重试 + 状态机”骨架,而是按 审计模型、脏数据扫描、回写验收 三段展开,方便团队把现场证据直接补进工程。

1. 审计模型

@Entity(tableName = "sync_audit")
data class SyncAuditEntity(
    @PrimaryKey val taskId: String,
    val localVersion: Long,
    val remoteVersion: Long?,
    val submitState: String,
    val writeBackState: String,
    val updatedAt: Long
)

fun SyncAuditEntity.isReadyForUi(): Boolean {
    return submitState == "remote_ok" && writeBackState == "accepted"
}

2. 脏数据扫描命令

adb shell am broadcast -a androidx.work.diagnostics.REQUEST_DIAGNOSTICS -p com.example.app
adb shell run-as com.example.app cat databases/app.db | head
adb logcat -d | findstr /I "sync_audit writeBackState remote_ok dirty"

3. 回写验收辅助代码

data class SyncAcceptanceRow(
    val taskId: String,
    val submitState: String,
    val writeBackState: String,
    val accepted: Boolean
)

fun buildAcceptanceRow(entity: SyncAuditEntity): SyncAcceptanceRow {
    return SyncAcceptanceRow(
        taskId = entity.taskId,
        submitState = entity.submitState,
        writeBackState = entity.writeBackState,
        accepted = entity.isReadyForUi()
    )
}

## Android 数据同步治理常见坑

第一类坑是把服务端 200 当成最终成功。实际上远端成功只说明对方接到了请求,不代表你本地已经把旧快照、安全字段和列表缓存一起换掉。第二类坑是把清脏任务和补偿任务写进同一个 Worker,导致现场根本分不清到底是补偿失败还是清理失败。第三类坑是 UI 直接监听数据库最新一条记录,结果中间态一写入,页面马上误报“同步完成”。

我更建议把 Android 数据同步治理 看成财务对账:提交是记账,回写是对账,页面展示是出报表。只要这三件事混在一起,问题就一定会在弱网、闪退恢复和多端改写时放大。

## Android 数据同步治理常见异常

1. 远端成功但本地回写没过

这类现场最迷惑,因为接口监控看起来一片绿,用户却还在反馈列表闪回或者编辑结果消失。排查时先不要急着看网络层,而是先核对 `remoteVersion` 是否写回、`writeBackState` 是否从 `pending` 进入 `accepted`。如果远端版本已经更新,本地却没有生成对应审计行,问题大概率在 repository 到落库这一段。

fun requireAccepted(entity: SyncAuditEntity) {
    check(entity.submitState == "remote_ok")
    check(entity.writeBackState == "accepted")
}

2. 清脏任务把有效数据一起卷走

另一类问题是补偿没挂,清理挂了。比如弱网恢复后清脏逻辑按时间窗口删除旧记录,却把还没完成回写验收的记录一起删掉,最后页面只能回退到更旧的缓存。这里不要只看 delete 条数,要把删除前后的任务 ID、版本号和 accepted 标记一起打出来,否则根本不知道删掉的是脏数据还是活数据。

adb logcat -d | findstr /I "dirty_cleanup accepted taskId localVersion remoteVersion"
adb shell dumpsys jobscheduler | findstr SyncCleanupWorker

## Android 数据同步治理最小可运行示例

最后这个样例专门服务于回归,不追求业务完整,只追求把“提交成功但未验收”和“提交成功且已验收”两种状态分开。只要团队能在本地一眼看出这两种状态的差别,很多同步误判都会当场少掉一半。

1. 最小状态对象

data class SyncSnapshot(
    val taskId: String,
    val submitState: String,
    val writeBackState: String
)

2. 本地验证入口

fun main() {
    val pending = SyncSnapshot("42", "remote_ok", "pending")
    val accepted = SyncSnapshot("43", "remote_ok", "accepted")
    println(pending)
    println(accepted)
}

3. 回归命令

./gradlew testDebugUnitTest
./gradlew connectedDebugAndroidTest
adb logcat -d | findstr /I "sync_audit accepted pending dirty_cleanup"

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注