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

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))
    }
}

额外检查两点:

  1. 确认ChatMessage类中的timestamp字段是Long类型,和数据库中的数值类型一致,避免类型不匹配导致排序失效。
  2. 去Firebase控制台的Database页面检查索引是否生效,当前规则中的.indexOn配置是正确的,但如果有警告需及时调整。

修改后,RecyclerView里的消息就会按时间戳正确排序了。

内容的提问来源于stack exchange,提问作者Tommy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:10:44