Firebase数据库用户知道,有两个用于监听数据的基本监听器:ValueEventListener
和ChildEventListener
。当我们监听一个对象时,它工作得很好,但是当我们监听某个集合时,它变得非常困难。
为了说明问题,让我们想象一下,我们有HackerNews供稿,例如, Firebase中的“posts”对象。
当然,我们的应用程序中有RecyclerView
用于显示帖子,我认为最好使用FirebaseUI,但是问题是,我们希望在更改服务器端或测试的情况下制作更抽象的应用程序。因此,我们将使用一些适配器,但这是另一个question。
正如我提到的,我们有两个监听器,问题是,哪个更好?
当我们使用ValueEventListener
时,我们将获得整个集合,但是如果发生任何更改(例如一个用户更改了帖子的内容),我们将必须重新加载整个数据,这意味着需要通过昂贵的网络传输来发送更多的字节。另一个问题是当我们使用多听者时,这是示例:
Post具有userId,但我们要显示其名称,因此在onDataChanged
方法中,我们已获取用户数据,如下所示:
postsReference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
Post post = data.getValue(Post.class);
usersReference.child(post.getUserId()).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Here we have user data
}
@Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
}
@Override
public void onCancelled(FirebaseError firebaseError) {
}
});
您可以看到,现在我们必须将每个帖子分别添加到
RecyclerView
,这将为我们提供提示,也许我们应该使用ChildEventListener
。所以现在当我们使用``ChildEventListener''时,问题是相同的-我们必须将每个帖子分别添加到
RecyclerView
中,但是当有人更改帖子内容时,firebase仅向我们发送此帖子,这意味着通过网络发送的数据更少。我们不喜欢将post单独添加到
RecyclerView
,因为:-很难添加加载指示器,因为我们不知道何时所有数据都会到来。
-用户不断刷新 View ,同时看到新帖子,而不是整个列表。
-很难对该集合进行排序,也许我们需要在适配器中进行排序。
问题
与集合一起使用firebase的最佳实践是什么,也许比我上面写的更好的解决方案?
编辑
数据方案如下所示:
"posts" : {
"123456" : {
"createdAt" : 1478696885622,
"content" : "This is post content",
"title" : "This is post title",
"userId" : "abc"
},
"789012" : {
"createdAt" : 1478696885622,
"content" : "This is post content 2",
"title" : "This is post title 2",
"userId" : "efg"
}
}
"users" : {
"abc" : {
"name" : "username1"
},
"efg" : {
"name" : "username2"
}
}
编辑2
我犯了一个错误->发生更改后,Firebase无法获取
ValueEventListener
中的整个数据。它仅获得“delta”,here就是证明。 最佳答案
这个问题有几个问题(即性能,进度指示器,处理新数据(例如对它们进行排序))。自然,您应该提出一种考虑了需求优先级的解决方案。 IMO ValueEventListener
和ChildEventListener
都有其用例:
ChildEventListener
通常是同步对象列表的推荐方法。甚至在documentation on working with lists中也提到了这一点:这是因为您的客户端仅收到具有特定更新(添加或删除)的更改后的子项,而不是每个更新上的整个列表。因此,它允许更细粒度地处理列表更新。
ValueEventListener
可能会更加有用。当您必须对RecyclerView
中的列表进行排序时,就是这种情况。通过获取整个列表,对其进行排序并刷新 View 的数据集,可以轻松得多。另一方面,使用ChildEventListener
进行排序会更加困难,因为每个更新事件只能访问列表中的一个特定子项。 从性能的角度来看,考虑到即使
ValueEventListener
在同步更新时也知道“增量”,所以我倾向于认为它仅在客户端效率较低,因为客户端必须这样做在整个列表上进行处理,但这仍然比在网络端效率低下要好得多。关于常量刷新和进度指示器,需要注意的最重要的一点是Firebase数据库是实时数据库,因此实时数据的恒定馈送是其固有特性。如果不需要更新事件,则可以简单地使用
addListenerForSingleValueEvent
方法仅读取一次数据。如果要在加载第一个快照时显示进度指示器,也可以使用此命令:// show progress indicator
postsReference.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// load initial data set
// hide progress indicator when done loading
}
...
});