Firebase+Kotlin聊天APP:哈希映射RecyclerView按timestamp排序失效
问题描述
跟随Firebase+Kotlin聊天APP教程开发应用,其他功能正常,但消息列表无法按timestamp字段排序。尝试使用orderByChild("timestamp")方法后仍未生效。
核心逻辑代码
// 创建存储每条消息的HashMap val latestMessagesMap = HashMap<String, ChatMessage>() // 刷新RecyclerView的方法 private fun refreshRecyclerViewMessages() { adapter.clear() latestMessagesMap.values.forEach { adapter.add(DaftarPesan(it)) } } // 监听最新消息的方法 private fun listenForLatestMessages() { val fromId = FirebaseAuth.getInstance().uid // 获取Firebase数据库路径 val ref = FirebaseDatabase.getInstance().getReference("/pesan-terakhir-pengguna/$fromId") // 尝试按timestamp排序每条消息 ref.orderByChild("timestamp").addChildEventListener(object: ChildEventListener { override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) { if (snapshot.exists()) { val chatMessage = snapshot.getValue(ChatMessage::class.java) ?: return latestMessagesMap[snapshot.key!!] = chatMessage // 更新Adapter refreshRecyclerViewMessages() } } override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) { val chatMessage = snapshot.getValue(ChatMessage::class.java) ?: return latestMessagesMap[snapshot.key!!] = chatMessage refreshRecyclerViewMessages() } override fun onChildRemoved(snapshot: DataSnapshot) {} override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {} override fun onCancelled(error: DatabaseError) {} }) } val adapter = GroupAdapter<GroupieViewHolder>()
ViewHolder类代码
// ViewHolder类 class DaftarPesan(val chatMessage: ChatMessage): Item<GroupieViewHolder>() { var chatPartnerUser: DataProfilUsaha? = null override fun bind(viewHolder: GroupieViewHolder, position: Int) { val chatPartnerId: String // 获取聊天对象的UID chatPartnerId = if (chatMessage.fromId == FirebaseAuth.getInstance().uid) { chatMessage.toId } else { chatMessage.fromId } // 获取聊天对象的头像和名称 val ref = FirebaseDatabase.getInstance().getReference("/profile/$chatPartnerId") ref.addListenerForSingleValueEvent(object: ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { if (snapshot.exists()) { chatPartnerUser = snapshot.getValue(ProfileData::class.java) // 设置发送者名称到TextView viewHolder.itemView.daftar_nama_pengirim_pesan_pedagang.text = chatPartnerUser?.namaUsaha // 设置发送者头像 val targetImageView = viewHolder.itemView.picture_daftar_pesan_pedagang Picasso.get().load(chatPartnerUser?.coverImageUrl).into(targetImageView) } } override fun onCancelled(error: DatabaseError) {} }) } }
数据库结构
"pesan-terakhir-pengguna": { "76UJp5x9WcdH94os8s9q952uI2": { "B4ftYzLXLcOsDiChjMAXOgnBnar1": { "fromId": "P6scAzLXLcOsDiChjMAXOgnBnar1", "id": "-NBXTFVboixKDNepRIhD", "text": "test", "timestamp": 1662730703, "toId": "76UJp5x9WcdH94os8s9q952uI2" } } }
Firebase安全规则
{ "rules": { ".read": auth != null, ".write": auth != null, "pesan-terakhir-pengguna": { "$uid": { ".indexOn": "timestamp" } } } }
RecyclerView创建时的日志信息
2022-09-11 20:46:46.974 18714-18714/com.example.gesit I/MSHandlerLifeCycle: isMultiSplitHandlerRequested: windowingMode=1 isFullscreen=true isPopOver=false isHidden=false skipActivityType=false isHandlerType=true this: DecorView@bfc1c4[MainScreen] 2022-09-11 20:46:46.992 18714-18714/com.example.gesit I/MSHandlerLifeCycle: isMultiSplitHandlerRequested: windowingMode=1 isFullscreen=true isPopOver=false isHidden=false skipActivityType=false isHandlerType=true this: DecorView@bfc1c4[MainScreen] 2022-09-11 20:46:47.020 18714-18714/com.example.gesit I/ViewRootImpl@fdb127d[MainScreen]: ViewPostIme pointer 1 2022-09-11 20:46:47.020 18714-18714/com.example.gesit I/MSHandlerLifeCycle: isMultiSplitHandlerRequested: windowingMode=1 isFullscreen=true isPopOver=false isHidden=false skipActivityType=false isHandlerType=true this: DecorView@bfc1c4[MainScreen]
解决方案
问题出在你用HashMap存储消息——HashMap是无序集合,就算Firebase已经按timestamp排序返回数据,你把数据存到HashMap后,遍历values()的时候顺序就乱了。
直接修改refreshRecyclerViewMessages方法,在遍历前对消息按timestamp降序排序(确保最新消息在最前面):
private fun refreshRecyclerViewMessages() { adapter.clear() // 按timestamp降序排序 latestMessagesMap.values.sortedByDescending { it.timestamp }.forEach { adapter.add(DaftarPesan(it)) } }
额外检查两点:
- 确认
ChatMessage类中的timestamp字段是Long类型,和数据库中的数值类型一致,避免类型不匹配导致排序失效。 - 去Firebase控制台的Database页面检查索引是否生效,当前规则中的
.indexOn配置是正确的,但如果有警告需及时调整。
修改后,RecyclerView里的消息就会按时间戳正确排序了。
内容的提问来源于stack exchange,提问作者Tommy
相关产品推荐
相关产品推荐

