Firebase缓存破坏了被找回孩子的顺序 [英] Firebase caching ruins order of retrieved children

查看:43
本文介绍了Firebase缓存破坏了被找回孩子的顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个聊天应用程序,并且在会话/聊天列表中,检索了聊天的最后一条消息,以便像在其他任何Messenger应用程序中一样显示它.

I am building a chat application, and on the conversations/chats list, the last message of a chat is retrieved in order to show it just like in any other messenger app.

但是,当打开聊天时,使用 messagesDbRef.orderByChild("time").addChildEventListener(...) onChildAdded 检索聊天的所有消息.立即调用code>回调以获取聊天的最后一条消息(在聊天列表中检索到的最后一条消息),该消息在 time 字段中具有最高的值,然后从数据库中的所有其他消息中进行检索. time 字段值的升序.

However, when the chat is opened, all messages of the chat are retrieved using messagesDbRef.orderByChild("time").addChildEventListener(...) and the onChildAdded callback is instantly called for the last message of the chat (the one retrieved on the chats list) which has the highest value for the time field, while all other messages are then retrieved from the database in the ascending order of the time field values.

在以1到5标记的消息示例中,这导致它们以[5,1,2,3,4]的顺序添加到RecyclerView中,最后一条消息是5.

In the example of messages labeled from 1 to 5, this causes them to be added to the RecyclerView in the order [5, 1, 2, 3, 4], with 5 being the last message.

但是,当聊天关闭并再次打开时,顺序是正确的[1、2、3、4、5].

However, when the chat is closed and opened again, the order is correct [1, 2, 3, 4, 5].

我该如何解决?打开聊天后,是否有一种方法可以强制重新加载所有消息?

How can I fix this? Is there a way to force reload all messages when the chat is opened?

这是一个重现该问题的最小工作示例:

Here is a minimal working example that reproduces the problem:

实时数据库中的数据:

testing: {
    abc123: {
        name: "first",
        time: 100
    },
    abc456: {
        name: "second",
        time: 200
    },
    abc789: {
        name: "third",
        time: 300
    }
}

代码:

final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("testing");

ref.child("abc789").addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        Stuff lastStuff = dataSnapshot.getValue(Stuff.class);
        Log.i("Testing", "retrived child " + lastStuff.name + " with time " + lastStuff.time);

        ref.orderByChild("time").addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                Stuff stuff = dataSnapshot.getValue(Stuff.class);
                Log.i("Testing", "name: " + stuff.name + ", time: " + stuff.time);
            }
            
            ...
});

这将产生输出:

I/测试:时间为300的儿童被抓获第三名

I/Testing: retrived child third with time 300

I/测试:名称:第三名,时间:300

I/Testing: name: third, time: 300

I/测试:名称:第一,时间:100

I/Testing: name: first, time: 100

I/测试:名称:秒,时间:200

I/Testing: name: second, time: 200

但是,我注意到如果我使用 addListenerForSingleValueEvent 而不是 addValueEventListener ,问题就消失了,顺序是正确的.我想我只是在应用程序中的某个地方打开了一个侦听器.

However, I noticed that if I use addListenerForSingleValueEvent instead of addValueEventListener, the problem is gone and the order is correct. I guess I just have a listener open somewhere in my application.

无论如何,我认为缓存的值不会在以后干扰检索顺序.

In any case, I don't think that the cached value should interfere with the order of retrieval later on.

推荐答案

该行为是由于Firebase确实已经在内存中拥有一个子级这一事实引起的.我最近在

The behavior is caused by the fact that Firebase already has one of the children in memory indeed. I explained it recently in an answer to Firebase query: Why is child_added called before the value query in the following code?.

但是只要您使用Firebase提供给您的所有信息,订单便会保留.为了显示这一点,我在您的代码中添加了一些额外的日志记录:

But the order is maintained, as long as you use all the information that Firebase gives you. To show this I added some additional logging to your code:

ref.child("abc789").addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        Log.i("Testing", "retrieved child " + dataSnapshot.child("name").getValue() + " with time " + dataSnapshot.child("time").getValue());

        ref.orderByChild("time").addChildEventListener(new ChildEventListener() {
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                Log.i("Testing", "ChildAdded: key "+dataSnapshot.getKey()+" name " + dataSnapshot.child("name").getValue() + ", time " + dataSnapshot.child("time").getValue()+" previousKey "+s);
            }

            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                Log.i("Testing", "ChildChanged: key "+dataSnapshot.getKey()+" name " + dataSnapshot.child("name").getValue() + ", time " + dataSnapshot.child("time").getValue()+" previousKey "+s);
            }

            public void onChildRemoved(DataSnapshot dataSnapshot) {
                Log.i("Testing", "ChildRemoved: key "+dataSnapshot.getKey()+" name " + dataSnapshot.child("name").getValue() + ", time " + dataSnapshot.child("time").getValue());
            }

            public void onChildMoved(DataSnapshot dataSnapshot, String s) {
                Log.i("Testing", "ChildMoved: key "+dataSnapshot.getKey()+" name " + dataSnapshot.child("name").getValue() + ", time " + dataSnapshot.child("time").getValue()+" previousKey "+s);
            }

            public void onCancelled(DatabaseError error) {
                System.err.println("Listener was cancelled "+error.toString());
            }

        });

    }

因此现在记录:

  1. 每个子快照的密钥
  2. 之前传递的密钥(在您的代码中称为 s )
  3. 它还会记录每个事件,尽管在此测试中我们仅使用 onChildAdded

运行此代码时,它将记录:

When you run this code it logs:

01-07 10:10:23.859 3706-3706/?I/测试:以时间300取得第三名的孩子

01-07 10:10:23.859 3706-3706/? I/Testing: retrieved child third with time 300

01-07 10:10:23.869 3706-3706/?I/测试:ChildAdded:密钥abc789名称第三次,时间300 previousKey null

01-07 10:10:23.869 3706-3706/? I/Testing: ChildAdded: key abc789 name third, time 300 previousKey null

01-07 10:10:23.932 3706-3706/?I/测试:ChildAdded:密钥abc123名称第一,时间100 previousKey null

01-07 10:10:23.932 3706-3706/? I/Testing: ChildAdded: key abc123 name first, time 100 previousKey null

01-07 10:10:23.933 3706-3706/?I/测试:ChildAdded:密钥abc456名称第二次,时间200先前密钥abc123

01-07 10:10:23.933 3706-3706/? I/Testing: ChildAdded: key abc456 name second, time 200 previousKey abc123

在这种情况下,使用 previousKey / s 参数是关键,如果我们根据这些信息重播如何构建UI/列表,这一点将变得很清楚.我建议您先参考[ onChildAdded()的参考文档,然后再执行这些步骤](

Using the previousKey/s argument is key in this case, which becomes clear if we replay how you build a UI/list out of this information. I recommend that you refer to the [reference documentation for onChildAdded() before going through these steps](https://firebase.google.com/docs/reference/android/com/google/firebase/database/ChildEventListener.html#onChildAdded(com.google.firebase.database.DataSnapshot, java.lang.String)):

  1. 您从一个空列表开始.
  2. 您收到子代 abc789 ,您将其作为唯一元素添加到列表中: [abc789]
  3. 您收到孩子 abc123 .由于它没有上一个密钥,因此可以将其添加到列表的开始中: [abc123,abc789]
  4. 您会收到子 abc456 ,并带有previousKey abc123 .当我们在 abc123 之后插入此代码时,我们得到最终列表: [abc123,abc456,abc789]
  1. You start with an empty list.
  2. You receive the child abc789, which you add to the list as its only element: [abc789]
  3. You receive the child abc123. Since it doesn't have a previous key, you add it to the start of the list: [abc123, abc789]
  4. You receive the child abc456, with previousKey abc123. When we insert this after abc123 we get the final list: [abc123, abc456, abc789]

因此,虽然 onChildAdded 调用发生的顺序确实有些令人惊讶,但传递给它们的信息使您可以为子代建立正确的UI/列表.

So while the order in which the onChildAdded calls happen is indeed somewhat surprising, the information passed into them allows you to build the correct UI/list for the children.

这篇关于Firebase缓存破坏了被找回孩子的顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆