Firestore消息监听中date字段首次返回null的问题求助
问题原因与解决方案
问题根源
Firestore 使用 @ServerTimestamp 时,客户端写入数据会触发两次快照更新:
- 第一次是本地写入确认:此时服务器时间还未生成,
date字段为null - 第二次是服务器同步完成:此时服务器已生成时间戳,
date字段才有有效值
你的消息监听器在第一次触发时直接使用了 null 的 date,导致后续排序或逻辑出错;而重新打开 Fragment 时,读取的是服务器已同步完成的完整数据,所以显示正常。
解决方案
1. 处理 date 为 null 的情况,避免空指针或排序异常
修改排序逻辑,将 date 为 null 的消息临时放在列表末尾,等服务器同步后自动修正:
// 替换原排序代码 messages.sortBy { it.date ?: Date(Long.MAX_VALUE) }
或者在创建 Message 时,给 date 一个临时默认值(比如当前客户端时间),等服务器同步后会自动覆盖:
var date = doc.getTimestamp(KEY_MESSAGE_DATE)?.toDate() ?: Date()
2. 优化监听器的消息去重逻辑
因为同一条消息会触发两次 ADDED 事件,原有的 if (message !in messages) 可能无法正确去重(两次的 date 不同)。建议改用 message.id 作为唯一标识判断:
// 替换原消息添加逻辑 val existingIndex = messages.indexOfFirst { it.id == id } if (existingIndex == -1) { messages.add(message) } else { // 如果是第二次更新(date 不为 null),替换原消息 if (date != null) { messages[existingIndex] = message } }
3. 确认 Message 类的 @ServerTimestamp 注解正确
确保实体类中对应的字段类型为可空的时间类型且注解正确:
import com.google.firebase.firestore.ServerTimestamp import java.util.Date data class Message( val text: String, val id: String, val receiverId: String, @ServerTimestamp val date: Date?, val isChecked: Boolean, val type: String )
修改后的完整监听器代码示例
database .collection(KEY_COLLECTION_USERS) .document(preferenceManager.getString(KEY_USER_ID)!!) .collection(KEY_COLLECTION_CHAT) .addSnapshotListener { value, error -> if (error != null || value == null) return@addSnapshotListener for (documentChange in value.documentChanges) { if (documentChange.type == DocumentChange.Type.ADDED) { val doc = documentChange.document if (doc.getString(KEY_MESSAGE_RECEIVER_ID) == receiverUser.uid) { val text = doc.getString(KEY_MESSAGE).toString() val id = doc.getString(KEY_MESSAGE_ID).toString() val receiverId = doc.getString(KEY_MESSAGE_RECEIVER_ID).toString() val date = doc.getTimestamp(KEY_MESSAGE_DATE)?.toDate() ?: Date() val isChecked = doc.getBoolean(KEY_MESSAGE_ISCHECKED)!! val type = doc.getString(KEY_TYPE_MESSAGE).toString() val message = Message(text, id, receiverId, date, isChecked, type) // 按ID去重,存在则替换(处理第二次更新) val existingIndex = messages.indexOfFirst { it.id == message.id } if (existingIndex == -1) { messages.add(message) } else { messages[existingIndex] = message } } } } messages.sortBy { it.date } val messagesTest = ArrayList(messages) userChatAdpater.updateList(messagesTest) if (messages.isNotEmpty()) { mBinding.rvUserChat.smoothScrollToPosition(messages.size - 1) } }
内容的提问来源于stack exchange,提问作者Danil
相关产品推荐
相关产品推荐

