Firebase数据库用户知道,有两个用于监听数据的基本监听器:ValueEventListenerChildEventListener。当我们监听一个对象时,它工作得很好,但是当我们监听某个集合时,它变得非常困难。

为了说明问题,让我们想象一下,我们有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 ValueEventListenerChildEventListener都有其用例:

  • 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
        }
    
        ...
    });
    

    10-06 16:14