1. 离线同步架构总览:Room、WorkManager、网络请求与数据一致性

离线同步是现代移动应用的核心能力——用户在网络不稳定或完全离线时仍能操作数据,恢复连接后自动将本地变更推送到远程服务器,同时拉取远程最新数据合并到本地。

本章从宏观视角解构离线同步的四大支柱:Room(本地持久化)、WorkManager(后台任务调度)、网络请求(数据传输层)以及数据一致性(冲突与合并策略)。后续所有章节都围绕这四者展开。

核心目标:构建一个“离线可写、在线自动同步、数据最终一致”的本地优先(Local-First)架构。
离线同步架构总览 用户界面 (UI) 增删改查操作 Room 本地数据库 SQLite 持久化 同步状态标记 WorkManager 调度 & 重试 约束(网络/电量) 网络请求 Retrofit / OkHttp JSON / REST 远程服务器 云 API / 后端 数据一致性协调 冲突检测 · 合并策略 关键数据流 ① 用户操作写入 Room(本地优先) ② WorkManager 检查待同步记录 → ④ 发起网络请求 → ⑤ 服务器响应 → ⑥ 一致性协调后更新 Room

1.1 四大组件职责

组件 职责 关键技术点
Room 本地数据持久化,提供离线操作能力;记录同步状态(SYNCED / PENDING / FAILED)。 Entity、Dao、TypeConverter、Migration
WorkManager 保证同步任务在满足约束时可靠执行,支持重试、背压、链式任务。 Worker、Constraints、OneTimeWorkRequest、Enqueue
网络请求 与远程服务器通信,序列化/反序列化数据。 Retrofit、OkHttp、Gson/Moshi、Interceptor
数据一致性 处理冲突(客户端/服务器版本差异),决定合并策略(LWW、CRDT、手动)。 时间戳、版本号、冲突回调、幂等性设计

1.2 实现流程(典型同步周期)

  1. 离线写入:用户创建/更新数据 → 直接写入 Room(状态标记 PENDING)。
  2. 触发同步:WorkManager 在满足网络约束时启动 SyncWorker。
  3. 读取待同步记录:从 Room 查询 syncStatus = PENDING 的数据。
  4. 发起请求:构建 Retrofit 请求,携带本地变更(JSON)。
  5. 处理响应:服务器返回确认或最新数据;更新本地状态为 SYNCED;若冲突则执行合并逻辑。
  6. 拉取远程数据:可选步骤,同步完成后拉取服务器最新完整数据写入 Room。
  7. 通知 UI:Room 的 LiveData/Flow 自动刷新界面。
实践建议:将同步状态字段设计为 @ColumnInfo(name = "sync_status") 枚举,配合 Room 的 @Query 高效检索待同步行。

1.3 核心代码骨架(各组件定义)

以下代码片段展示离线同步架构中各组件的典型声明,详细实现将在后续章节展开。

// ---- Room Entity ----
@Entity(tableName = "tasks")
public class Task {
    @PrimaryKey
    @NonNull
    public String id;             // 全局唯一 ID(UUID)
    public String title;
    public boolean completed;
    @ColumnInfo(name = "sync_status")
    public SyncStatus syncStatus; // PENDING / SYNCED / FAILED
    public long lastModified;     // 时间戳,用于冲突检测
}

// ---- Room Dao ----
@Dao
public interface TaskDao {
    @Query("SELECT * FROM tasks WHERE syncStatus = :status")
    List<Task> getTasksBySyncStatus(SyncStatus status);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void upsert(Task task);

    @Query("UPDATE tasks SET syncStatus = :status WHERE id = :id")
    void updateSyncStatus(String id, SyncStatus status);
}

// ---- WorkManager Worker ----
public class SyncWorker extends Worker {
    @NonNull
    @Override
    public Result doWork() {
        // 1. 从 Room 读取 PENDING 数据
        // 2. 构建 Retrofit 请求发送
        // 3. 根据响应更新本地状态
        // 4. 返回 Result.success() 或 Result.retry()
        return Result.success();
    }
}

// ---- 约束与调度 ----
Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build();

OneTimeWorkRequest syncWork = new OneTimeWorkRequest.Builder(SyncWorker.class)
        .setConstraints(constraints)
        .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS)
        .build();

WorkManager.getInstance(context).enqueue(syncWork);

1.4 数据一致性策略一览

策略 描述 适用场景
Last-Writer-Wins (LWW) 以服务器或客户端中较新的时间戳为准,直接覆盖。 简单字段如标题、备注;允许丢失旧版本。
CRDT 无冲突复制数据类型,自动合并(如计数器、集合)。 协作编辑、点赞数、标签。
手动合并 检测冲突后保留两个版本,提示用户选择或融合。 关键业务数据如订单、金额。
版本向量 维护版本列表,检测因果冲突。 多设备复杂同步。
常见陷阱:
• 忽略同步状态索引导致查询性能骤降。
• 未处理 WorkManager 的 Result.retry() 指数退避,造成过多重复请求。
• 时间戳精度不足(秒级)导致冲突误判,推荐使用毫秒或单调递增版本号。

1.5 调试与验证要点

  • 启用 Stetho 或 Database Inspector 观察 Room 中 sync_status 变化。
  • 使用 adb shell am broadcast -a android.intent.action.BOOT_COMPLETED 触发 WorkManager 重跑。
  • 在 SyncWorker 内添加日志输出 WorkManager.getInstance(applicationContext).getWorkInfosByTag(tag) 状态。
  • 模拟网络不稳定:Android Emulator 中的“Network -> Airplane Mode”或限制带宽。

本章为离线同步架构打下了全局基础,后续各章节将逐一深入每个组件的实现细节与优化技巧。读者应已理解:Room 负责本地存储与状态标记,WorkManager 负责可靠调度,网络请求承担数据传输,数据一致性策略决定最终结果。四者协同构成了健壮的离线同步管线。