如何降低Firestore读操作量 实现仅拉取新增数据无需全量重读
解决Firestore仅拉取新增数据、降低读取次数的方案
现有代码问题说明
- 第一种缓存判断逻辑存在缺陷:仅当缓存为空时才拉取服务端数据,若缓存已有旧数据,后续永远不会拉取新数据,这是拉不到新内容的核心原因
- 第二种直接调用
get()的方案默认优先拉取服务端全量数据,每次调用都会产生全量读取的成本
可行实现方案
核心逻辑为首次全量拉取数据后,仅监听并拉取符合条件的新增增量数据,完全避免重复读取已有文档。
实现步骤
- 给所有文档新增
createTime字段,写入数据时赋值为Firestore服务器时间,避免客户端时间偏差 - 首次启动时先读取本地缓存数据,记录已有数据的最新时间戳
- 后续仅监听创建时间大于该时间戳的文档,只处理新增类型的变更
示例代码
// 存储已有数据的最新时间戳,作为增量拉取的判断条件 private Timestamp lastDocTimestamp = null; private List<Item> items = new ArrayList<>(); private YourAdapter mAdapter; // 初始化数据加载 private void initDataLoad() { // 优先读取本地缓存数据 query.get(Source.CACHE).addOnCompleteListener(cacheTask -> { if (cacheTask.isSuccessful()) { QuerySnapshot cacheSnapshot = cacheTask.getResult(); if (!cacheSnapshot.isEmpty()) { // 加载缓存中的已有数据 for (QueryDocumentSnapshot doc : cacheSnapshot) { items.add(doc.toObject(Item.class)); } // 记录缓存中最新数据的创建时间 lastDocTimestamp = cacheSnapshot.getDocuments() .get(cacheSnapshot.size() - 1) .getTimestamp("createTime"); mAdapter.notifyDataSetChanged(); } // 开启增量监听 startNewDataListener(); } else { // 缓存为空时首次全量拉取服务端数据 query.get(Source.SERVER).addOnCompleteListener(serverTask -> { if (serverTask.isSuccessful()) { QuerySnapshot serverSnapshot = serverTask.getResult(); for (QueryDocumentSnapshot doc : serverSnapshot) { items.add(doc.toObject(Item.class)); } if (!serverSnapshot.isEmpty()) { lastDocTimestamp = serverSnapshot.getDocuments() .get(serverSnapshot.size() - 1) .getTimestamp("createTime"); } mAdapter.notifyDataSetChanged(); startNewDataListener(); } }); } }); } // 仅监听新增数据的快照监听器 private void startNewDataListener() { Query incrementalQuery; if (lastDocTimestamp != null) { // 仅查询创建时间晚于已有数据的文档,只会返回新增内容 incrementalQuery = query.whereGreaterThan("createTime", lastDocTimestamp); } else { incrementalQuery = query; } // 绑定快照监听,仅拉取符合条件的变更数据 incrementalQuery.addSnapshotListener((snapshot, error) -> { if (error != null || snapshot == null || snapshot.isEmpty()) { return; } // 只处理新增类型的文档变更,可根据需求新增修改、删除的处理逻辑 for (DocumentChange change : snapshot.getDocumentChanges()) { if (change.getType() == DocumentChange.Type.ADDED) { Item newItem = change.getDocument().toObject(Item.class); items.add(newItem); // 更新最新时间戳 lastDocTimestamp = change.getDocument().getTimestamp("createTime"); } } mAdapter.notifyDataSetChanged(); }); }
方案优势
- 首次加载后,后续每次有新数据仅产生1次读取操作,无全量重复读取成本
- 自带离线缓存能力,离线状态下的操作恢复上线后会自动同步,无需额外适配
内容的提问来源于stack exchange,提问作者Cavid Cavidd
相关产品推荐
相关产品推荐

