Android Firestore获取Receiver Room ID时RecyclerView消息重复排查
问题分析:RecyclerView消息重复与双房间数据获取优化
除了获取发送方房间(sender room)的数据外,还需要获取接收方房间(receiver room)的ID以实现双方消息删除功能,但当前RecyclerView出现消息重复问题,需排查代码错误。
原代码
private fun setDataToAdapter(){ firestore.collection("Chats").document(senderRoom!!).collection("messages").orderBy("time") .addSnapshotListener { value, _ -> if (!value?.isEmpty!!) { messages.clear() for (document in value.documents) { val coMsg = document.data?.get("message").toString() val coId = document.data?.get("senderId").toString() val msgId = document.id //Using Date // Get Calendar val calendarMap = document.data?.get("date") as Map<*, *> val curTimestamp = calendarMap["time"] as com.google.firebase.Timestamp val millis = curTimestamp.seconds * 1000 + curTimestamp.nanoseconds / 1000000 val ss = SimpleDateFormat("hh:mm a", Locale.getDefault()) val netCal = Date(millis) val msgTime = ss.format(netCal).toString() // Get Day val chatDay = document.data?.get("day").toString() // Get Full Date val fullDateStamp = calendarMap["time"] as com.google.firebase.Timestamp val dateInMillis = fullDateStamp.seconds * 1000 + fullDateStamp.nanoseconds / 1000 val sdf = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()) val netAllDate = Date(dateInMillis) val fullDate = sdf.format(netAllDate).toString() val type = document.data?.get("type").toString() firestore.collection("Chats").document(receiverRoom!!) .collection("messages").orderBy("time").addSnapshotListener { value1, _ -> if (!value1?.isEmpty!!) { for (document1 in value.documents) { val id = document1.id val coMsgT = SingleChatData(coMsg, coId, msgId, msgTime, chatDay, fullDate, type, id) messages.add(coMsgT) } recyclerView.post { // Call smooth scroll recyclerView.smoothScrollToPosition(adapter.itemCount - 1) } adapter.notifyDataSetChanged() } } } } } }
核心错误点
- 嵌套监听+循环重复添加:在发送方房间的每条消息循环里,都注册了接收方房间的快照监听,每次发送方有新消息时,都会触发接收方的监听;同时遍历的是
value.documents(发送方的消息列表)而非value1.documents(接收方的),导致发送方消息被重复添加N次(N为发送方消息条数)。 - 未清理旧监听:每次调用
setDataToAdapter都会新增监听,旧监听不会自动移除,多次触发回调后加剧消息重复问题。 - 数据逻辑冗余:聊天消息在发送方和接收方房间是镜像存储的,无需同时拉取两边数据,只需拉取其中一方的消息,同时关联对应房间的消息ID即可。
修复后的代码示例
// 全局变量存储监听,方便页面销毁时移除 private var senderMsgListener: ListenerRegistration? = null private fun setDataToAdapter(){ // 先移除旧监听,避免重复回调 senderMsgListener?.remove() messages.clear() // 只拉取发送方房间的消息,同时获取关联的接收方消息ID senderMsgListener = firestore.collection("Chats").document(senderRoom!!) .collection("messages").orderBy("time") .addSnapshotListener { value, _ -> if (value?.isEmpty != true) { messages.clear() for (document in value.documents) { val coMsg = document.data?.get("message").toString() val coId = document.data?.get("senderId").toString() val senderMsgId = document.id // 假设发送消息时,已将接收方房间的消息ID存入当前消息的receiverMsgId字段 val receiverMsgId = document.data?.get("receiverMsgId").toString() // 日期处理逻辑保留 val calendarMap = document.data?.get("date") as Map<*, *> val curTimestamp = calendarMap["time"] as com.google.firebase.Timestamp val millis = curTimestamp.seconds * 1000 + curTimestamp.nanoseconds / 1000000 val ss = SimpleDateFormat("hh:mm a", Locale.getDefault()) val msgTime = ss.format(Date(millis)).toString() val chatDay = document.data?.get("day").toString() val fullDateStamp = calendarMap["time"] as com.google.firebase.Timestamp val dateInMillis = fullDateStamp.seconds * 1000 + fullDateStamp.nanoseconds / 1000 val sdf = SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()) val fullDate = sdf.format(Date(dateInMillis)).toString() val type = document.data?.get("type").toString() // 同时携带发送方和接收方的消息ID,用于后续删除操作 val chatData = SingleChatData(coMsg, coId, senderMsgId, msgTime, chatDay, fullDate, type, receiverMsgId) messages.add(chatData) } recyclerView.post { recyclerView.smoothScrollToPosition(adapter.itemCount - 1) } adapter.notifyDataSetChanged() } } } // 页面销毁时移除监听,防止内存泄漏 override fun onDestroy() { super.onDestroy() senderMsgListener?.remove() }
额外优化建议
- 消息存储规范:发送消息时,生成一个全局唯一的
msgUid,同时存入发送方和接收方房间的消息文档中,删除时通过msgUid批量删除两边的消息,无需单独存储对方房间的消息ID。 - 避免嵌套监听:嵌套监听会导致逻辑混乱和性能损耗,如需多数据源关联,建议使用批量获取(
get())而非实时监听,或拆分监听逻辑。 - 数据去重兜底:如果必须拉取多源数据,添加消息时通过
msgUid等唯一标识做去重判断,避免重复添加。
内容的提问来源于stack exchange,提问作者Mahmoud Nabil
相关产品推荐
相关产品推荐

