我正在构建一个聊天应用程序,并在对话/聊天列表中检索聊天的最后一条消息,以便像在任何其他 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());
}
});
}
现在记录:
- 每个子快照的 key
- 传递的前一个 key (在您的代码中称为
s
) - 它还会记录每个事件,虽然在这个测试中我们只使用
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)):
- 您从一个空列表开始。
- 您收到 child
abc789
,您将其作为唯一元素添加到列表中:[abc789]
- 您收到 child
abc123
。由于它没有以前的 key ,您将它添加到列表的开始:[abc123, abc789]
- 您收到带有 previousKey
abc123
的 childabc456
。当我们在abc123
之后插入它时,我们得到最终列表:[abc123, abc456, abc789]
因此,虽然 onChildAdded
调用发生的顺序确实有点令人惊讶,但传递给它们的信息允许您为 child 构建正确的 UI/列表。
关于android - Firebase 缓存破坏了检索到的 child 的顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48139076/