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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 07:58:07