5

The documentation for DiffUtil sugeruje generowanie DiffUtil.DiffResult na wątku tła ze względu na potencjalne długie czasy obliczeń. Wydaje się to zły pomysł do mnie, ponieważ nitka mogła działać na danych nieaktualnych w sytuacji takiej jak następuje (zakładając list dostępu jest bezpieczne dla wątków):RecyclerView and DiffUtil - koszmar współbieżności

  1. Dodawanie danych do list i powiadomić adapterowi
  2. Need zastąpić list z newList miałoby diff z pewnymi dodatkami, a niektóre przeprowadzki
  3. połączeń DiffUtil.calculateDiff w tle i uzyskać DiffResult dla list i newList i wysyłać wiadomości do głównego wątku, który będzie używał newList i zadzwonić DiffResult.dispatchUpdatesTo
  4. Przed że wiadomość jest obsługiwana, użytkownik wykonuje czynność na głównym wątku, który powoduje mutacje list
  5. wiadomości jest obsługiwany, newList jest ustawiona jako nowe źródło danych i DiffResult.dispatchUpdatesTo jest uruchamiany powodując niespójne widok dane źródłowe + utrata wszelkich mutacji, ponieważ DiffResults obliczono

dobrze, że nie jest dobrze, więc zmieńmy zaczynając od kroku 3:

  1. Zestaw newList jako nowe źródło danych, nazywamy DiffUtil.calculateDiff w tle i uzyskać DiffResult dla list i newList i wysyłać wiadomości do głównego wątku, który będzie zadzwonić DiffResult.dispatchUpdatesTo
  2. Przed że wiadomość jest obsługiwana, użytkownik wykonuje czynność na głównym wątek, który powoduje mutacje newList i powiadamia zasilacza, co powoduje niespójność widok danych, ponieważ DiffResult.dispatchUpdatesTo nie został jeszcze

nazywany jest więcej wariacje na ten temat, ale nie są dobre. Wydaje się, że jedynym sposobem na niezawodne użycie DiffUtil z dużym zestawem danych i zestaw zmian jest wyłączenie lub kolejkowanie wszystkich aktualizacji do czasu wywołania DiffResult.dispatchUpdatesTo.

Czy brakuje mi czegoś, co sprawiłoby, że powyższe byłoby fałszywe?

+0

przede wszystkim, ile przedmiotów masz? jeśli to nie kilka tysięcy, dlaczego się martwić? – pskink

+0

@pskink jak dokumentacja stwierdza: '1000 pozycji i 200 modyfikacji bez ruchów: 13.54 ms, mediana: 13.36 ms'. 1000 przedmiotów to niewiele. Biorąc 13,54 ms, aby to zrobić, pozostawiamy nam 2,5 ms, aby wykonać wszystkie pozostałe prace dla tej ramki, lub będziemy mieli opuszczoną ramkę. 2,5 ms to mało czasu. – Eliezer

+0

ok, więc użyj 'dispatchUpdatesTo (ListUpdateCallback updateCallback)', a nie 'dispatchUpdatesTo (Adapter adapter)', w ten sposób będziesz kontrolować sposób obsługi aktualizacji (możesz pominąć aktualizacje elementów, które zostały zmienione przez użytkownika w międzyczasie) – pskink

Odpowiedz

2

Zobacz BatchingListUpdateCallback. Jest to klasa, która jest opakowana w główne wywołanie zwrotne i gdy pojawi się wiele zmian na liście, wsadowyListCallback powiadomi tylko raz główne wywołanie zwrotne.

https://developer.android.com/reference/android/support/v7/util/BatchingListUpdateCallback.html

EDIT

przykro mi moja odpowiedź nie jest poprawna.

Zajrzałem do kodu źródłowego DiffUtil. i widzę, że mimo to BatchingListUpdateCallback jest używana w metodzie dispatchUpdatesTo.

public void dispatchUpdatesTo(ListUpdateCallback updateCallback) { 
     final BatchingListUpdateCallback batchingCallback; 
     if (updateCallback instanceof BatchingListUpdateCallback) { 
      batchingCallback = (BatchingListUpdateCallback) updateCallback; 
     } else { 
      batchingCallback = new BatchingListUpdateCallback(updateCallback); 
      // replace updateCallback with a batching callback and override references to 
      // updateCallback so that we don't call it directly by mistake 
      //noinspection UnusedAssignment 
      updateCallback = batchingCallback; 
     } 

Ale jako wektor do właściwej drodze to może być przydatna :)

+0

Interesujące. Więc kiedy pojawi się nowa lista, zawijamy ją za pomocą 'BatchListUpdateCallback' i wysyłamy do niej wyniki DiffUtils. Nadal występują problemy ze współbieżnością, ale to mogłoby ułatwić wdrożenie potencjalnego rozwiązania. – Eliezer

+0

Nie byłem w pełni poprawny. Edytowałem swoją odpowiedź. – Sirelon