我正在构建一个聊天应用程序,并在对话/聊天列表中,检索聊天的最后一条消息,以便像在任何其他信使应用程序中一样显示它。
但是,当聊天打开时,将使用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);
}
...
});
这会产生输出:
我/测试:在时间300的情况下重新获得第三名儿童
我/测试:名称:第三,时间:300
I / Testing:name:first,time:100
我/测试:名称:秒,时间:200
但是,我注意到如果我使用addListenerForSingleValueEvent
代替addValueEventListener
,问题就会消失,订单也是正确的。我想我的应用程序中只有一个监听器可以打开。
在任何情况下,我都认为缓存的值不会影响以后的检索顺序。
答案 0 :(得分:1)
这种行为是由于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());
}
});
}
所以现在记录:
s
)onChildAdded
运行此代码时,它会记录:
01-07 10:10:23.859 3706-3706 /?我/测试:在时间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:首先输入密钥abc123,时间100 previousKey null
01-07 10:10:23.933 3706-3706 /?我/测试:ChildAdded:键abc456名称第二,时间200 previousKey abc123
在这种情况下,使用previousKey
/ s
参数是关键,如果我们重播您如何使用此信息构建UI /列表,这一点就变得清晰了。我建议您在完成这些步骤之前参考[{1}}的参考文档](https://firebase.google.com/docs/reference/android/com/google/firebase/database/ChildEventListener.html#onChildAdded(com.google.firebase.database.DataSnapshot,java.lang.String)):
onChildAdded()
,您将其作为唯一元素添加到列表中:abc789
[abc789]
。由于它没有以前的密钥,您可以将其添加到列表的开始:abc123
[abc123, abc789]
,其中包含previousKey abc456
。当我们在abc123
之后插入此内容时,我们会得到最终列表:abc123
因此,虽然[abc123, abc456, abc789]
调用发生的顺序确实有点令人惊讶,但传递给它们的信息允许您为子项构建正确的UI /列表。