Android中的RecyclerView的ValueEventListener与ChildEventListener

时间:2016-12-07 15:02:49

标签: java android firebase firebase-realtime-database

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

要指明问题,让我们想象一下我们有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才是证据。

4 个答案:

答案 0 :(得分:19)

这个问题有几个问题(即绩效,进度指标,处理新数据,如排序)。当然,您应该提出一个考虑到您的要求优先级的解决方案。 IMO ValueEventListenerChildEventListener都有其用例:

  1. ChildEventListener通常是同步对象列表的推荐方式。甚至在documentation on working with lists

    中也提到了这一点
      

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

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

  2. 当您需要在修改孩子时处理整个列表时,
  3. ValueEventListener会更有用。必须在RecyclerView中对列表进行排序时就是这种情况。通过获取整个列表,对其进行排序并刷新视图的数据集,可以更轻松地完成此操作。另一方面,使用ChildEventListener,进行排序会更加困难,因为每次更新事件只能访问列表中的一个特定子项。

  4. 效果的角度来看,即使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数据库位置。

  1. 使用 vel (ValueEventListener)使用 dbref 中的当前数据列表填充 recyclerview 。这样的事情:vel = dbref.addValueEventListener(vel);
  2. 我有 dbref 的当前数据列表后,我从 dbref dbref.removeEventListener(vel);中删除了侦听器 vel 。 如果您使用dbref.addListenerForSingleValueEvent(vel);,则无需执行此操作 在第1步。
  3. 撰写查询查询,以便从现在开始过滤在 dbref 处插入的新帖子。像这样的东西: query = dbref.orderByChild(post.createdat).startAt(System.currentTimeMillis())
  4. cel (ChildEventListener)附加到查询以仅接收新帖子:query.addChildEventListener(cel);

答案 2 :(得分:2)

在Firebase上处理收集数据时,您应该使用:

  • onChildAdded
  • onChildChanged
  • onChildRemoved
  • onChildMoved

您可能不需要所有这些,但我发现通常onChildAddedonChildRemoved通常是必不可少的。您必须对适配器中的数据更改进行排序和跟踪,并将其传递回每个更新的子项上的RecyclerView。

相比之下,你可以使用ValueEventListener并简单地更新整个列表,但是就像你说的那样,这意味着该列表中的每次更新都会导致集合中的所有对象被发送给你,这将花费你您的用户数据并在Firebase上花费更多带宽。如果您的数据经常更新,则不建议这样做。

Source

答案 3 :(得分:2)

我会在任何一天使用onchildAdded监听器,在这种情况下,它有几个优点,首先你已经想出了网络操作,假设有100个帖子,即使一个被改变,你将获得所有这些回调。而在onChildListener中,您将获得唯一更改的帖子的回调。所以你可以做的是将firebase的回调映射到像这样的回收器视图方法:: -

onChildAdded - &gt;您必须将dataSnapshot转换为您的类并调用recyclerView.notifyItemAdded()

onChildRemoved - &gt; recyclerView.notifyItemRemoved(INT)

onChildChanged - &gt; recyclerView.notifyItemChanged(INT)

onChildMoved - &gt; (你可能不需要这个,它是为了订购/优先)