使用 firebase 按最新消息对聊天列表进行排序

Posted

技术标签:

【中文标题】使用 firebase 按最新消息对聊天列表进行排序【英文标题】:Sort chat-list by the most recent message with firebase 【发布时间】:2021-03-06 04:43:38 【问题描述】:

我不知道为什么我遇到了 chatList 没有按最后消息时间或最近消息排序的问题。我尝试将timestamp 存储在数据库中并使用 orderChildBy 时间戳,但它仍然无法正常工作。不工作意味着列表不会在每条消息后排序,并继续将列表显示为在第一条消息之后排序。

看图,聊天是怎么乱的!

这是我在 sendMessage 的 ChatActiviy 中的 firebaseDatabase 中创建 chatList 的方式:

    val timeAgo = Date().time

    val myTimeMap = HashMap<String, Any?>()
        myTimeMap["timestamp"] = timeAgo
        myTimeMap["id"] = friendId

    val friendTimeMap = HashMap<String, Any?>()
        friendTimeMap["timestamp"] = timeAgo
        friendTimeMap["id"] = currentUserID

    val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).child(friendId)
        chatListSenderReference.keepSynced(true)
        chatListSenderReference.addListenerForSingleValueEvent(object : ValueEventListener
              override fun onCancelled(p0: DatabaseError) 
              
              override fun onDataChange(p0: DataSnapshot) 
                       if(!p0.exists())
                             chatListSenderReference.updateChildren(friendTimeMap)
                       
    val chatListReceiverReference = dbRef.child("ChatList").child(friendId).child(currentUserID)
        chatListReceiverReference.updateChildren(myTimeMap)
        
    )

在 recyclerView 中检索聊天列表时,我试图获取每个用户的用户详细信息,这些用户在数据库中显示为 currentUser 的子项。 (聊天列表>>CurrentUserId)

已编辑

  private fun retrieveChatList() 

    usersChatList = ArrayList()
    val userRef = dbRef.child("ChatList").child(currentUserID).orderByChild("timestamp")
    userRef.addValueEventListener(object : ValueEventListener
    
        override fun onCancelled(error: DatabaseError) 
        

        override fun onDataChange(snapshot: DataSnapshot)
        
            (usersChatList as ArrayList<String>).clear()
            if (snapshot.exists())
                for (dataSnapshot in snapshot.children)
                    val userUid = dataSnapshot.key
                    if (userUid != null) 
                        (usersChatList as ArrayList<String>).add(userUid)
                    
                
                readChatList()
            
        
    )


private fun readChatList() 
    mUsers = ArrayList()
    val userRef = FirebaseFirestore.getInstance().collection("Users")
    userRef.get()
            .addOnSuccessListener  queryDocumentSnapshots ->
                mUsers?.clear()
                for (documentSnapshot in queryDocumentSnapshots) 
                    val user = documentSnapshot.toObject(User::class.java)
                    for (id in usersChatList!!)
                        if (user.getUid() == id)
                            (mUsers as ArrayList<User>).add(user)
                        
                    
                
                retrieveGroupChatList()
                chatListAdapter?.notifyDataSetChanged()
                chatListAdapter = context?.let  ChatListAdapter(it, (mUsers as ArrayList<User>), true) 
                recyclerViewChatList.adapter = chatListAdapter

            .addOnFailureListener  e ->
                Log.d(ContentValues.TAG, "UserAdapter-retrieveUsers: ", e)
            


这是用于好友信息的 chatListAdapter

private fun friendInfo(fullName: TextView, profileImage: CircleImageView, uid: String) 
        val userRef = FirebaseFirestore.getInstance().collection("Users").document(uid)
        userRef.get()
                .addOnSuccessListener 
                    if (it != null && it.exists()) 
                        val user = it.toObject(User::class.java)
                Picasso.get().load(user?.getImage()).placeholder(R.drawable.default_pro_pic).into(profileImage)
                fullName.text = user?.getFullName()
            
        
    

这是实时数据库的图片,有一个模型类为ChatList,每次发送或接收消息时时间戳都会更新。

还有另一张用户在 Firestore 中的图片,其模型类为 Users

解决方案

我有一个可行的解决方案,在这里我在Firestore Users 集合中创建或更新一个字段作为 lastMessageTimestamp ,以便用户现在可以按 lastMessageTimestamp 排序。

   val timeAgo = Date().time
    
        val myFSMap = HashMap<String, Any?>()
            myFSMap["timestamp"] = timeAgo
    
        val friendFSMap = HashMap<String, Any?>()
            friendFSMap["timestamp"] = timeAgo
    
      //firebase chatlist references.
        val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).child(friendId)
        val chatListReceiverReference = dbRef.child("ChatList").child(friendId).child(currentUserID)

      //Firestore Users references.
        val chatListSenderRef = fStore.collection("Users").document(currentUserID)
        val chatListReceiverRef = fStore.collection("Users").document(friendId)
    
        chatListSenderReference.addListenerForSingleValueEvent(object : ValueEventListener
           override fun onDataChange(p0: DataSnapshot) 
                 if(!p0.exists())
                    chatListSenderReference.setValue(friendId)
                    //update the timestamp in Users collection
                    chatListSenderRef.update(myFSMap)
                 
                    chatListReceiverReference.setValue(currentUserID)
                    chatListReceiverRef.update(friendFSMap)

           override fun onCancelled(p0: DatabaseError) 
               
            
        )

在阅读时,我使用 orderBy 表示用户

 val userRef = FirebaseFirestore.getInstance().collection("Users").orderBy("lastMessageTimestamp" , Query.Direction.ASCENDING)

但这不是完整的解决方案,因为似乎我每次在消息传递时都会读取和写入 lastMessageTimestamp,这可以将Firebase Billing 增加到巨大的可怕数字。 所以我仍然需要一个解决方案。

【问题讨论】:

这会有所帮助吗:Sorting firebase database by timestamp under a key 和 Work with Lists of Data on android @MethkalKhalawi 我试过你的建议,但它的工作原理相同没有区别。我会设置赏金然后尝试回答以获得更多声誉。 好吧抱歉@mr.groot有点不清楚,我删除了我的答案,因为它不是你需要的,我调试了你共享的代码,它工作正常,.orderByChild("timestamp ") 工作完美,如果我删除该行,所有项目都将按照保存在 Firebase 数据库中的顺序检索,如果将其添加回时间戳获取的所有项目。 我还在 Firebase 控制台中手动更改了一些时间戳,并且排序再次正确。 snapshot.children.forEach 中的每个值都是正确的。因此,只需检查最终的 mUsers 数据,然后再将它们添加到适配器中,它们的顺序是什么,或者在将它们添加到适配器之后您对这些数据做了什么。 @MariosP 谢谢,我很感激你的奉献精神,帮助你写了一个非常明确的答案。我现在就试试你的建议。请继续尝试回答这个问题。 【参考方案1】:

简单的技巧是消息的 orderBy id。因为firebase生成的id基于实时+几个因素。因此,让我们尝试按 Id 而不是您的时间戳排序。 (注意:只是由firebase生成的id)

【讨论】:

没有这样的 id 兄弟,我说的是与我聊天的用户的聊天列表,而不是消息。【参考方案2】:

enter code here看到你的帖子不知道它是否有用到 android 数据库,然后使用与 userid 匹配的聊天列表 firebase 数据库中的 Id 检索身份

您的代码可能如下所示

从聊天列表 firebase 数据库中读取 检索发件人 ID 和时间 使用 id 在 android 数据库中检索已添加的人员信息 您的模型应包含用于从数据库中检索时间的变量 然后将所有添加到列表中 之后使用比较器根据时间对数组列表/列表进行排序 然后通知适配器变化

` ...... userDao = UserDatabase.getUserDatabase(requireContext()).userDao();

private void sortChatList() 

    reference.child("chatlist").child(firebaseUser.getUid()).orderByChild("time").addValueEventListener(new ValueEventListener() 
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) 

            list.clear();;
            for (DataSnapshot snapshot : dataSnapshot.getChildren())
                String userID = Objects.requireNonNull(snapshot.child("chatid").getValue()).toString();
                 String time =  snapshot.child("time").getValue().toString();
                
                Chatlist chatlist = new Chatlist();


                UserDB userDB = userDao.getAll(userID);
                chatlist.setDate(time);
                chatlist.setUserName(userDB.getUserName());
                chatlist.setUserID(userID);
               


                list.add(chatlist);

            
       
            Collections.sort(list, new Comparator<Chatlist>() 
                @Override
                public int compare(Chatlist o1, Chatlist o2) 
                    return Integer.valueOf(o2.getTime().compareTo(o1.getTime()));
                
            );
            if (adapter != null) 

                adapter.notifyDataSetChanged();

            .........

`

【讨论】:

以上是关于使用 firebase 按最新消息对聊天列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

Firebase - 按顺序获取数据列表(最后一个数据)

Firebase-按顺序获取数据列表(最后一个数据在前)

是否有必要在将聊天消息存储到 Firebase 之前对其进行加密?

如何抓取多条路径并对它们进行排序并将结果带回firebase?

附加 Firebase 聊天消息

用于一对一消息传递的 Firebase 数据库结构?