android - Firebase 缓存破坏了检索到的 child 的顺序

标签 android firebase firebase-realtime-database

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

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

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

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

我该如何解决这个问题?有没有办法在打开聊天时强制重新加载所有消息?

编辑:

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

实时数据库中的数据:

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/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

但是,我注意到如果我使用addListenerForSingleValueEvent 而不是addValueEventListener,问题就消失了并且顺序是正确的。我想我只是在应用程序的某处打开了一个监听器。

无论如何,我认为缓存的值不应该影响以后的检索顺序。

最佳答案

该行为是由于 Firebase 确实已经在内存中有一个子项这一事实引起的。我最近在对 Firebase query: Why is child_added called before the value query in the following code? 的回答中对此进行了解释。

但是只要您使用 Firebase 提供给您的所有信息,订单就会保持不变。为了证明这一点,我在您的代码中添加了一些额外的日志记录:

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. 每个子快照的 key
  2. 传递的前一个 key (在您的代码中称为 s)
  3. 它还会记录每个事件,虽然在这个测试中我们只使用 onChildAdded

当您运行此代码时,它会记录:

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

在这种情况下,使用 previousKey/s 参数是关键,如果我们重播如何根据这些信息构建 UI/列表,这一点就会变得很清楚。我建议您在执行这些步骤之前引用 [onChildAdded() 的引用文档](https://firebase.google.com/docs/reference/android/com/google/firebase/database/ChildEventListener.html#onChildAdded(com.google.firebase.database.DataSnapshot、java.lang.String)):

  1. 您从一个空列表开始。
  2. 您收到 child abc789,您将其作为唯一元素添加到列表中:[abc789]
  3. 您收到 child abc123。由于它没有以前的 key ,您将它添加到列表的开始:[abc123, abc789]
  4. 您收到带有 previousKey abc123 的 child abc456。当我们在 abc123 之后插入它时,我们得到最终列表:[abc123, abc456, abc789]

因此,虽然 onChildAdded 调用发生的顺序确实有点令人惊讶,但传递给它们的信息允许您为 child 构建正确的 UI/列表。

关于android - Firebase 缓存破坏了检索到的 child 的顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48139076/

相关文章:

android - onTouch 在 getX android 中给出奇怪的结果

android - YouTube 上的视频上传 - Android

firebase - 在 Firebase 数据库管理中根据条件/子值删除所有数据的好方法?

javascript - Firebasefunctions.auth.user().onCreate不触发

android sqlite 在 2 个不同的列中插入 2 个值

android - 如何测量物体或面部与相机的距离

android - 安装失败 'cordova-plugin-firebase' : CordovaError: Uh oh

ios - 未从 firebase 添加 TableView 事件

javascript - 构建此 JSON 数据的更好方法?

Android 应用程序在向下滚动时崩溃