Firebase caching ruins order of retrieved children

谁都会走 提交于 2019-12-24 09:22:16

问题


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.

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.

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.

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?

EDIT:

Here is a minimal working example that reproduces the problem:

Data in realtime database:

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

Code:

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

            ...
});

This produces the output:

I/Testing: retrived child third with time 300

I/Testing: name: third, time: 300

I/Testing: name: first, time: 100

I/Testing: name: second, time: 200

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.


回答1:


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?.

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

        });

    }

So this now logs:

  1. the key for each child snapshot
  2. the previous key (called s in your code) that is passed
  3. it's also logging each event, although in this test we only use onChildAdded

When you run this code it logs:

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/Testing: ChildAdded: key abc789 name third, time 300 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/Testing: ChildAdded: key abc456 name second, time 200 previousKey abc123

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. 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]

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.



来源:https://stackoverflow.com/questions/48139076/firebase-caching-ruins-order-of-retrieved-children

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!