Android Firestore监听更新RecyclerView重复追加问题修复
解决Firestore实时监听导致RecyclerView重复数据的问题
核心问题分析
遍历设备ID注册快照监听时,每次快照触发都直接往gsmModels列表追加数据,未做覆盖或去重处理;同时缺少监听实例的统一管理,可能导致重复注册监听,进一步加剧数据重复问题。
修复方案(小改动+逻辑优化)
1. 新增监听与数据映射管理
在MainActivity中添加两个全局集合,分别管理每个设备的监听实例和最新数据:
// 存储每个设备ID对应的监听引用,用于后续移除监听 private Map<String, ListenerRegistration> deviceListenerMap = new HashMap<>(); // 存储每个设备ID对应的最新数据,保证数据唯一不重复 private Map<String, GsmModel> deviceDataMap = new HashMap<>();
2. 优化设备ID遍历与监听注册逻辑
获取到keysList(设备ID列表)后,先清理无效监听,再注册新监听或更新已有数据:
// 第一步:移除已不在keysList中的设备监听,释放资源 Iterator<Map.Entry<String, ListenerRegistration>> iterator = deviceListenerMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, ListenerRegistration> entry = iterator.next(); String deviceId = entry.getKey(); if (!keysList.contains(deviceId)) { entry.getValue().remove(); // 移除监听 iterator.remove(); // 从映射中删除 deviceDataMap.remove(deviceId); // 删除对应无效数据 } } // 第二步:遍历设备ID,注册新监听或复用已有监听 for (String deviceId : keysList) { if (!deviceListenerMap.containsKey(deviceId)) { ListenerRegistration registration = FirebaseFirestore.getInstance() .collection("devices") .document(deviceId) .addSnapshotListener((documentSnapshot, e) -> { if (e != null) { Log.e("Firestore", "监听失败", e); return; } if (documentSnapshot != null && documentSnapshot.exists()) { GsmModel model = documentSnapshot.toObject(GsmModel.class); if (model != null) { // 覆盖旧数据,保证每个设备只有一条最新记录 deviceDataMap.put(deviceId, model); updateRecyclerView(); } } else { // 设备文档不存在,移除对应数据 deviceDataMap.remove(deviceId); updateRecyclerView(); } }); deviceListenerMap.put(deviceId, registration); } }
3. 统一RecyclerView数据更新方法
避免重复追加,直接用最新数据替换原有列表:
private void updateRecyclerView() { // 将映射中的数据转为列表,可按需排序 List<GsmModel> updatedList = new ArrayList<>(deviceDataMap.values()); // 清空原有列表并替换为新数据 gsmModels.clear(); gsmModels.addAll(updatedList); // 通知适配器更新 adapter.notifyDataSetChanged(); // 进阶优化:若使用ListAdapter,可替换为局部刷新 // adapter.submitList(new ArrayList<>(updatedList)); }
4. 页面销毁时清理监听
在onDestroy方法中移除所有监听,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); for (ListenerRegistration registration : deviceListenerMap.values()) { registration.remove(); } deviceListenerMap.clear(); deviceDataMap.clear(); }
5. Adapter无需大改动
确保Adapter基于当前列表的最新数据渲染即可:
@Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { GsmModel model = gsmModels.get(position); holder.deviceName.setText(model.getDeviceName()); // 原有按钮逻辑保持不变 }
关键说明
deviceDataMap保证每个设备的数据唯一,每次快照更新都会覆盖旧数据,从根源避免重复。deviceListenerMap避免重复注册同一设备的监听,同时方便在设备ID列表变化时清理无效监听。- 若追求更流畅的UI体验,可将常规Adapter替换为
ListAdapter,配合DiffUtil实现局部刷新,减少全量更新的性能消耗。
内容的提问来源于stack exchange,提问作者Ankhi
相关产品推荐
相关产品推荐

