ValueEventListener vs ChildEventListener for RecyclerView in Android

Firebase 数据库用户知道有两个用于侦听 Data 的基本侦听器:和 。当我们听一个对象时,它效果很好,但是当我们听一些集合时,它会变得非常困难。ValueEventListenerChildEventListener

为了指定问题,让我们想象一下我们有HackerNews提要,我们听例如Firebase中的“posts”对象。

当然,我们的应用程序中有用于显示帖子的好主意,我认为使用FirebaseUI是个好主意,但问题是,我们希望在更改服务器端或测试的情况下制作更抽象的应用程序。因此,我们将使用一些适配器,但这是另一个问题RecyclerView

正如我所提到的,我们有两个听众,问题是哪个更好

当我们使用时,我们将获得整个集合,但是如果发生任何更改,例如一个用户更改了帖子的内容,我们将不得不重新加载整个数据,这意味着通过昂贵的网络传输发送更多的字节。另一个问题是当我们使用多列表器时,下面是一个例子: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) {
    }
});

您可以看到,现在我们必须单独添加每个帖子,这将为我们提供线索,也许我们应该使用 。RecyclerViewChildEventListener

所以现在当我们使用“ChildEventListener”时,问题是一样的 - 我们必须单独添加每个帖子,但是当有人更改帖子内容时,firebase只向我们发送这一个帖子,这意味着通过网络的数据更少。RecyclerView

我们不喜欢将帖子单独添加到 ,因为例如: - 很难添加加载指标,因为我们不知道何时所有数据都出现。- 用户获得不断刷新的视图,而新帖子出现而不是整个列表变得可见。- 很难对该集合进行排序,我们可能必须在适配器中执行此操作。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不会获取整个数据。它只得到“delta”,这是证据。ValueEventListener


答案 1

这个问题有几个问题(即性能,进度指示器,处理新数据,例如对它们进行排序)。当然,您应该提出一个考虑到您要求优先级的解决方案。IMO两者都有他们的用例:ValueEventListenerChildEventListener

  1. ChildEventListener通常是同步对象列表的推荐方法。这甚至在有关使用列表的文档中也提到过:

    使用列表时,应用程序应侦听子事件,而不是用于单个对象的值事件。

    这是因为您的客户端仅接收具有特定更新(添加或删除)的已更改子级,而不是每次更新时的整个列表。因此,它允许对列表的更新进行更精细的处理。

  2. ValueEventListener当您需要在修改孩子时处理整个列表时,可能会更有用。当您必须在 .通过获取整个列表,对其进行排序并刷新视图的数据集,执行此操作要容易得多。另一方面,使用 a 更困难地进行排序,因为每个更新事件只能访问列表中的一个特定子级。RecyclerViewChildEventListener

性能的角度来看,考虑到即使在同步更新时也知道“增量”,我倾向于认为它仅在客户端效率较低,因为客户端必须对整个列表进行处理,但这仍然比在网络端效率低下要好得多。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
    }

    ...
});

答案 2

我有同样确切的问题(与Android中相比)。ValueEventListenerChildEventListenerRecyclerView

我使用的解决方案是以这种方式将两者结合起来:

假设 dbref 指向我的帖子所在的 firebase db 位置。

  1. 使用 vel(ValueEventListener)在回收器视图中填充 dbref 上的当前数据列表。像这样:vel = dbref.addValueEventListener(vel);
  2. 一旦我在dbref上拥有当前数据的列表,我就从dbref中删除侦听器vel:。如果在步骤 1 中使用,则不需要这样做。dbref.removeEventListener(vel);dbref.addListenerForSingleValueEvent(vel);
  3. 编写一个查询查询,以从现在开始筛选在 dbref 中插入的新帖子。像这样:query = dbref.orderByChild(post.createdat).startAt(System.currentTimeMillis())
  4. 附加 cel (ChildEventListener) 以查询以仅接收新帖子:query.addChildEventListener(cel);

推荐