Подтвердить что ты не робот

ValueEventListener vs ChildEventListener для RecyclerView в Android

Пользователи базы данных Firebase знают, что для прослушивания присутствуют два основных прослушивателя Data: ValueEventListener и ChildEventListener. Он отлично работает, когда мы слушаем один объект, но становится довольно сложно, когда мы слушаем какую-то коллекцию.

Чтобы задать вопрос, представьте, что у нас есть канал HackerNews, и мы слушаем, например. Объект "posts" в Firebase.

Конечно, у нас есть 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 отправляет нам только этот пост, что означает меньше данных через сеть.

Нам не нравится добавлять запись в 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, когда что-то изменилось. Он получает только "дельта", здесь является доказательством.

4b9b3361

Ответ 1

В этом вопросе есть несколько проблем (а именно: показатель производительности, прогресс, обработка новых данных, таких как их сортировка). Естественно, вы должны придумать решение, которое учитывает приоритет ваших требований. ИМО как ValueEventListener, так и ChildEventListener имеют свои варианты использования:

  • ChildEventListener обычно является рекомендуемым способом синхронизации списков объектов. Это даже упоминается в документации при работе со списками:

    При работе со списками ваше приложение должно прослушивать дочерние события, а не события значения, используемые для отдельных объектов.

    Это связано с тем, что ваш клиент получает только измененный дочерний элемент с конкретным обновлением (добавлением или удалением) в отличие от всего списка при каждом обновлении. Таким образом, он позволяет более гранулировать уровень обработки обновлений в списке.

  • ValueEventListener может быть более полезным, когда вам нужно обработать весь список при модификации ребенка. Это тот случай, когда вам нужно отсортировать список в RecyclerView. Гораздо проще сделать это, получив весь список, отсортировав его и обновив набор данных представления. С другой стороны, с помощью 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
    }

    ...
});

Ответ 2

У меня была такая же точная проблема (ValueEventListener vs ChildEventListener для RecyclerView в Android).

Решение, которое я использовал, состояло в том, чтобы объединить оба способа:

Предположим, что dbref указывает на местоположение базы данных firebase, где находятся мои сообщения.

  • Используйте vel (ValueEventListener), чтобы заполнить recyclerview список текущих данных dbref. Что-то вроде этого: vel = dbref.addValueEventListener(vel);
  • Как только у меня есть список текущих данных в dbref, я удаляю прослушиватель vel из dbref: dbref.removeEventListener(vel);. Это не обязательно, если вы использовали dbref.addListenerForSingleValueEvent(vel); на шаге 1.
  • Напишите запрос для фильтрации новых сообщений, вставленных с dbref. Что-то вроде этого: query = dbref.orderByChild(post.createdat).startAt(System.currentTimeMillis())
  • Прикрепите cel (ChildEventListener) к запросу для получения только новых сообщений: query.addChildEventListener(cel);

Ответ 3

При работе с данными коллекции Firebase вы должны использовать:

  • onChildAdded
  • onChildChanged
  • onChildRemoved
  • onChildMoved

Вам может не понадобиться все это, но я обнаружил, что обычно onChildAdded и onChildRemoved часто необходимы. Вам придется сортировать и отслеживать изменения данных в вашем адаптере и передавать их обратно в RecyclerView для каждого обновленного дочернего элемента.

Напротив, вы можете использовать ValueEventListener и просто обновлять весь список, как вы сказали, это означает, что каждое обновление в этом списке приведет к тому, что все объекты в коллекции будут отправлены вам, что будет стоить вашему пользователю данных и стоить вам больше полосы пропускания на Firebase. Это не рекомендуется, если ваши данные будут часто обновляться.

Источник

Ответ 4

В любом случае я бы использовал onchildAdded прослушиватель, есть несколько преимуществ, во-первых, вы выяснили сетевую операцию, которая предполагает, что есть 100 сообщений, и даже если кто-то изменится, вы получите обратный вызов из всех них. Если в onChildListener вы получите обратный вызов единственного сообщения, которое было изменено. Итак, что вы можете сделать, это сопоставить обратные вызовы firebase с методами просмотра recycler, как это: -

onChildAdded → вы должны указать dataSnap в свой класс и вызвать recyclerView.notifyItemAdded()

onChildRemoved → recyclerView.notifyItemRemoved(int)

onChildChanged → recyclerView.notifyItemChanged(int)

onChildMoved → (вам, вероятно, это не нужно, это для упорядочения/приоритета)