You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android(Java)如何结合SQLite/Room与Firebase实现数据同步

实现Room与Firebase的双向同步方案(Java)

针对你的需求,这里提供一套基于Firebase Auth + Firestore的双向同步实现方案,完全满足多设备数据同步、离线操作、数据恢复的要求:

一、核心同步规则梳理

先明确几个必须遵守的同步逻辑:

  • 登录状态:本地与云端双向同步,本地变更实时推送到云端,云端变更实时更新到本地
  • 离线状态:仅操作本地Room数据库,待联网后自动触发同步
  • 数据恢复:登录时检测本地数据缺失,直接从Firebase拉取全量数据覆盖/填充本地

二、分步实现指南

1. 依赖配置

在项目build.gradle(模块级)中添加必要依赖:

// Room
implementation "androidx.room:room-runtime:2.5.0"
annotationProcessor "androidx.room:room-compiler:2.5.0"
// Firebase Auth + Firestore
implementation "com.google.firebase:firebase-auth:22.3.0"
implementation "com.google.firebase:firebase-firestore:24.10.0"

2. 用户身份与数据隔离

用Firebase Auth做用户身份验证,每个用户的数据存储在Firebase的独立节点下,比如Firestore中用users/{uid}/your-collection的结构,确保多设备数据不会混淆。

3. Room数据库改造

给实体类添加同步标记字段,用于追踪数据状态:

@Entity(tableName = "tasks")
public class Task {
    @PrimaryKey(autoGenerate = true)
    public long id;
    public String title;
    public String description;
    // 记录数据最后修改时间,用于冲突判断
    public long lastUpdatedTimestamp;
    // 标记是否已同步到云端
    public boolean isSynced;

    // 构造函数、Getter、Setter
}

在DAO中添加获取未同步数据的方法:

@Dao
public interface TaskDao {
    // 常规CRUD方法
    @Insert
    void insertTask(Task task);
    @Update
    void updateTask(Task task);
    @Delete
    void deleteTask(Task task);
    @Query("SELECT * FROM tasks")
    LiveData<List<Task>> getAllTasks();

    // 获取未同步数据
    @Query("SELECT * FROM tasks WHERE isSynced = false")
    List<Task> getUnsyncedTasks();
    // 获取本地最新数据时间戳
    @Query("SELECT MAX(lastUpdatedTimestamp) FROM tasks")
    Long getLocalMaxTimestamp();
}

4. 登录时的同步逻辑

登录成功后分两种场景处理:

// 登录成功后调用该方法
private void syncOnLogin(String userId) {
    // 监听本地数据是否为空
    taskDao.getAllTasks().observe(this, localTasks -> {
        if (localTasks.isEmpty()) {
            // 本地无数据,从Firebase拉取全量数据
            FirebaseFirestore.getInstance()
                    .collection("users").document(userId)
                    .collection("tasks")
                    .get()
                    .addOnSuccessListener(snapshots -> {
                        List<Task> cloudTasks = new ArrayList<>();
                        for (DocumentSnapshot doc : snapshots) {
                            Task task = doc.toObject(Task.class);
                            task.isSynced = true;
                            cloudTasks.add(task);
                        }
                        // 批量插入本地
                        taskDao.insertTasks(cloudTasks); // 需要在DAO中添加批量插入方法
                    });
        } else {
            // 先同步本地未完成同步的数据到云端
            syncUnsyncedTasksToCloud(userId);
            // 再拉取云端比本地新的数据
            pullNewCloudData(userId);
        }
    });
}

// 同步本地未同步数据到云端
private void syncUnsyncedTasksToCloud(String userId) {
    List<Task> unsyncedTasks = taskDao.getUnsyncedTasks();
    for (Task task : unsyncedTasks) {
        FirebaseFirestore.getInstance()
                .collection("users").document(userId)
                .collection("tasks")
                .document(String.valueOf(task.id))
                .set(task)
                .addOnSuccessListener(aVoid -> {
                    task.isSynced = true;
                    task.lastUpdatedTimestamp = System.currentTimeMillis();
                    taskDao.updateTask(task);
                });
    }
}

// 拉取云端最新数据
private void pullNewCloudData(String userId) {
    Long localMaxTime = taskDao.getLocalMaxTimestamp();
    if (localMaxTime == null) localMaxTime = 0L;

    FirebaseFirestore.getInstance()
            .collection("users").document(userId)
            .collection("tasks")
            .whereGreaterThan("lastUpdatedTimestamp", localMaxTime)
            .get()
            .addOnSuccessListener(snapshots -> {
                for (DocumentSnapshot doc : snapshots) {
                    Task cloudTask = doc.toObject(Task.class);
                    Task localTask = taskDao.getTaskById(cloudTask.id); // DAO中添加单条查询方法

                    if (localTask == null) {
                        // 本地无该数据,直接插入
                        cloudTask.isSynced = true;
                        taskDao.insertTask(cloudTask);
                    } else {
                        // 对比时间戳,保留最新版本
                        if (cloudTask.lastUpdatedTimestamp > localTask.lastUpdatedTimestamp) {
                            taskDao.updateTask(cloudTask);
                        }
                    }
                }
            });
}

5. 本地数据变更的实时同步

用Room的LiveData监听本地数据变化,登录状态下自动同步:

// 监听本地数据变化
taskDao.getAllTasks().observe(this, tasks -> {
    FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
    if (currentUser != null) {
        for (Task task : tasks) {
            if (!task.isSynced) {
                syncSingleTaskToCloud(task, currentUser.getUid());
            }
        }
    }
});

// 同步单个任务到云端
private void syncSingleTaskToCloud(Task task, String userId) {
    FirebaseFirestore.getInstance()
            .collection("users").document(userId)
            .collection("tasks")
            .document(String.valueOf(task.id))
            .set(task)
            .addOnSuccessListener(aVoid -> {
                task.isSynced = true;
                task.lastUpdatedTimestamp = System.currentTimeMillis();
                taskDao.updateTask(task);
            })
            .addOnFailureListener(e -> {
                // 同步失败,保留isSynced为false,待下次联网重试
            });
}

6. 云端数据变更的实时监听

登录后开启Firestore快照监听,实时同步云端变更到本地:

// 登录后启动云端监听
private void startCloudChangeListener(String userId) {
    FirebaseFirestore.getInstance()
            .collection("users").document(userId)
            .collection("tasks")
            .addSnapshotListener((snapshots, error) -> {
                if (error != null || snapshots == null) return;

                for (DocumentChange change : snapshots.getDocumentChanges()) {
                    Task cloudTask = change.getDocument().toObject(Task.class);
                    switch (change.getType()) {
                        case ADDED:
                        case MODIFIED:
                            Task localTask = taskDao.getTaskById(cloudTask.id);
                            // 避免循环同步:仅当云端数据更新时间晚于本地时才更新
                            if (localTask == null || cloudTask.lastUpdatedTimestamp > localTask.lastUpdatedTimestamp) {
                                cloudTask.isSynced = true;
                                taskDao.updateTask(cloudTask);
                            }
                            break;
                        case REMOVED:
                            taskDao.deleteTask(cloudTask);
                            break;
                    }
                }
            });
}

7. 离线处理

Firebase默认开启离线持久化,离线时的云端操作会自动缓存,待联网后执行;Room本身就是本地存储,离线状态下正常读写即可,无需额外处理。

三、关键注意事项

  • 冲突处理:采用时间戳优先策略,始终保留最新修改的数据
  • 避免循环同步:云端监听时,通过时间戳判断是否需要更新本地,防止本地推送到云端的数据被重复拉回
  • 批量同步:大量未同步数据时,建议批量处理以减少网络请求次数
  • 数据恢复:用户删除本地数据库后,登录时会触发全量拉取云端数据的逻辑,自动完成恢复

内容的提问来源于stack exchange,提问作者Muhammad Arslan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:01:11