Firebase数据库用户知道有两个用于收听数据的基本侦听器:ValueEventListener
和ChildEventListener
。当我们听一个对象时它很有用,但是当我们听一些收集时变得非常困难。
要指明问题,让我们想象一下我们有HackerNews提要,我们会听取例如"帖子" Firebase中的对象。
当然我们在应用中有RecyclerView
用于显示帖子,我认为好主意是使用FirebaseUI,但问题是我们想要在更改服务器端或测试时制作更抽象的应用程序。所以我们会使用一些适配器,但这是另一个question。
我提到有两个听众,问题是哪个更好?
当我们使用ValueEventListener
时,我们将获得整个集合,但是如果有任何更改,例如一个用户更改了帖子的内容,我们将不得不重新加载整个数据,这意味着通过昂贵的网络传输发送更多字节。另一个问题是当我们使用multilisteners时,这是示例:
发布了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只向我们发送这一帖子,这意味着更少的数据通过网络。
我们不想单独向RecyclerView
添加帖子,因为例如:
- 添加加载指示器很困难,因为我们不知道所有数据何时到来。
- 用户可以获得不断刷新的视图,而新的帖子而不是整个列表变得可见。
- 很难对该集合进行排序,我们必须在适配器中这样做。
问题
将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才是证据。
答案 0 :(得分:19)
这个问题有几个问题(即绩效,进度指标,处理新数据,如排序)。当然,您应该提出一个考虑到您的要求优先级的解决方案。 IMO ValueEventListener
和ChildEventListener
都有其用例:
ChildEventListener
通常是同步对象列表的推荐方式。甚至在documentation on working with lists:
使用列表时,应用程序应该监听子事件,而不是单个对象使用的值事件。
这是因为您的客户端仅接收具有特定更新(添加或删除)的已更改子项,而不是每次更新时的整个列表。因此,它允许更精细地处理列表的更新。
ValueEventListener
会更有用。必须在RecyclerView
中对列表进行排序时就是这种情况。通过获取整个列表,对其进行排序并刷新视图的数据集,可以更轻松地完成此操作。另一方面,使用ChildEventListener
,进行排序会更加困难,因为每次更新事件只能访问列表中的一个特定子项。
从效果的角度来看,即使ValueEventListener
在同步更新时意识到“delta”,我也会认为它效率较低< em>仅在客户端,因为客户端必须对整个列表进行处理,但这仍然比在网络端具有低效率要好得多。
关于不断刷新和进度指标,最重要的是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
}
...
});
答案 1 :(得分:4)
我遇到了同样的问题(Android中的ValueEventListener
ChildEventListener
vs RecyclerView
。
我使用的解决方案是以这种方式结合:
假设 dbref 指向我的帖子所在的firebase数据库位置。
vel = dbref.addValueEventListener(vel);
dbref.removeEventListener(vel);
中删除了侦听器 vel 。
如果您使用dbref.addListenerForSingleValueEvent(vel);
,则无需执行此操作
在第1步。query = dbref.orderByChild(post.createdat).startAt(System.currentTimeMillis())
query.addChildEventListener(cel);
答案 2 :(得分:2)
在Firebase上处理收集数据时,您应该使用:
onChildAdded
onChildChanged
onChildRemoved
onChildMoved
您可能不需要所有这些,但我发现通常onChildAdded
和onChildRemoved
通常是必不可少的。您必须对适配器中的数据更改进行排序和跟踪,并将其传递回每个更新的子项上的RecyclerView。
相比之下,你可以使用ValueEventListener
并简单地更新整个列表,但是就像你说的那样,这意味着该列表中的每次更新都会导致集合中的所有对象被发送给你,这将花费你您的用户数据并在Firebase上花费更多带宽。如果您的数据经常更新,则不建议这样做。
答案 3 :(得分:2)
我会在任何一天使用onchildAdded监听器,在这种情况下,它有几个优点,首先你已经想出了网络操作,假设有100个帖子,即使一个被改变,你将获得所有这些回调。而在onChildListener中,您将获得唯一更改的帖子的回调。所以你可以做的是将firebase的回调映射到像这样的回收器视图方法:: -
onChildAdded
- &gt;您必须将dataSnapshot转换为您的类并调用recyclerView.notifyItemAdded()
onChildRemoved
- &gt; recyclerView.notifyItemRemoved(INT)
onChildChanged
- &gt; recyclerView.notifyItemChanged(INT)
onChildMoved
- &gt; (你可能不需要这个,它是为了订购/优先)