Nasz dział kontroli jakości wykrył błąd: podczas obracania urządzenia z Androidem (Droid Turbo) miała miejsce następująca awaria związana z RecyclerView :
java.lang.IndexOutOfBoundsException: Wykryto niespójność. Nieprawidłowa pozycja 2 pozycji (przesunięcie: 2). Stan: 3
Dla mnie wygląda to na wewnętrzny błąd w RecyclerView, ponieważ nie mogę sobie wyobrazić, aby to było spowodowane bezpośrednio przez nasz kod ...
Czy ktoś napotkał ten problem?
Jakie byłoby rozwiązanie?
Brutalnym obejściem może być złapanie wyjątku, kiedy to nastąpi, i odtworzenie instancji RecyclverView od zera, aby uniknąć pozostawienia uszkodzonego stanu.
Ale jeśli to możliwe, chciałbym lepiej zrozumieć problem (i być może naprawić go u źródła), zamiast maskować.
Błąd nie jest łatwy do odtworzenia, ale jest fatalny w skutkach.
Pełny ślad stosu:
W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
E/AndroidRuntime( 7546): FATAL EXCEPTION: main
E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
E/AndroidRuntime( 7546): at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
E/AndroidRuntime( 7546): at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
E/AndroidRuntime( 7546): at andro
Odpowiedzi:
Miałem (prawdopodobnie) związany z tym problem - wejście w nową instancję działania za pomocą RecyclerView, ale z mniejszym adapterem spowodowało to dla mnie awarię.
RecyclerView.dispatchLayout()
może spróbować wyciągnąć przedmioty ze złomu przed wezwaniemmRecycler.clearOldPositions()
. Konsekwencją tego jest to, że wyciągał przedmioty ze wspólnej puli, które miały pozycje wyższe niż rozmiar adaptera.Na szczęście robi to tylko wtedy, gdy
PredictiveAnimations
są włączone, więc moim rozwiązaniem było podklasęGridLayoutManager
(LinearLayoutManager
ma ten sam problem i „naprawę”) i zastąpienie,supportsPredictiveItemAnimations()
aby zwrócić false:źródło
W moim przypadku (usuń / wstaw dane w mojej strukturze danych) musiałem wyczyścić pulę recyklingu, a następnie powiadomić o zmianie zestawu danych!
mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();
źródło
W takim przypadku użyj
notifyDataSetChanged()
zamiastnotifyItem...
tego.źródło
notifyItem...
i poprawianie, które zacznie działać zamiast ponownego renderowania wszystkich elementów.Rozwiązałem to, opóźniając
mRecycler.setAdapter(itemsAdapter)
kasę po dodaniu wszystkich elementów do adapteramRecycler.addAll(items)
i działało. Nie mam pojęcia, dlaczego to zrobiłem, zacząłem od kodu biblioteki, który przejrzałem i zobaczyłem te wiersze w „niewłaściwej kolejności”, ale jestem pewien, że to jest to, proszę, jeśli ktoś może to potwierdzić, wyjaśnić, dlaczego to jest więc? Nie jestem pewien, czy jest to nawet poprawna odpowiedźźródło
swapAdapter(adapter, true)
zamiastsetAdapter(adapter)
i to pomogło.Miałem podobny problem, ale nie dokładnie taki sam. W moim przypadku w 1 punkcie wyczyściłem tablicę, która została przekazana do widoku recyklera
i nie wywoływanie powiadomieniaDataSetChanged, ponieważ nie chciałem, aby widok recyklera natychmiast wyczyścił widoki. Ponownie wypełniałem tablicę mObjects w AsyncTask.
źródło
Miałem ten sam problem z recyclinglerView Więc właśnie powiadomiłem adapter o zmianie zestawu danych zaraz po wyczyszczeniu listy.
źródło
Mam ten sam problem. Wystąpiło, gdy przewijałem szybko i dzwoniłem do API i aktualizowałem dane. Po wypróbowaniu wszystkich rzeczy, aby zapobiec awarii, znalazłem rozwiązanie.
To będzie działać.
źródło
Zmieniam dane
RecyclerView
w tleThread
. Mam to samoException
co OP. Dodałem to po zmianie danych:Mam nadzieję, że to pomoże
źródło
view.recycler_view.post
, użyłemnotifyItemInserted
. W moim przypadku był to już wątek interfejsu użytkownika.Ten błąd występuje, gdy lista w adapterze zostaje wyczyszczona podczas przewijania przez użytkownika, co powoduje zmianę pozycji uchwytu elementu, utratę referencji między listą a pozycją w interfejsie użytkownika, błąd pojawia się w następnym żądaniu „replaceDataSetChanged” .
Naprawić:
Sprawdź metodę listy aktualizacji. Jeśli zrobisz coś takiego
Jak naprawić. Utwórz nowy obiekt listy do przetwarzania bufora i następnie przypisz ponownie do listy głównej
Dzięki Nhan Cao za tę wspaniałą pomoc :)
źródło
Mój problem zniknął po zmodyfikowaniu mojej
Adapter
implementacji w celu użycia kopii tablicy elementów zamiast odwołania.setItems()
Metoda jest wywoływana za każdym razem mamy nowych elementów do pokazania wRecyclerView
.Zamiast:
Zrobiłem:
źródło
suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true);
i to jest mój konstruktorpublic CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mam do czynienia z tą samą sytuacją. Zostało to rozwiązane poprzez dodanie kodów przed wyczyszczeniem kolekcji.
mRecyclerView.getRecycledViewPool().clear();
źródło
W moim przypadku aktualizowałem elementy i dzwoniłem
notifyDataSetChanged
w wątku innym niż interfejs użytkownika. Przez większość czasu działało, ale gdy wiele zmian nastąpiło szybko, zawalił się. Kiedy to zrobiłem, w zasadziepotem przestało się zawieszać.
źródło
Musisz tylko wyczyścić listę,
OnPostExecute()
a nie robić toPull to Refresh
Odkryłem, że dzieje się tak, gdy przewijasz podczas ściągania, aby odświeżyć , ponieważ wyczyściłem listę przed
async task
, w wyniku czegojava.lang.IndexOutOfBoundsException: Inconsistency detected.
W ten sposób nie skończysz z niekonsekwencją
źródło
Można to również powiązać z wielokrotnym ustawianiem adaptera jednocześnie. Miałem metodę wywołania zwrotnego, która była wyzwalana 5-6 razy w tym samym czasie i ustawiałem adapter w tym wywołaniu zwrotnym, aby RecycledViewPool nie mógł obsługiwać wszystkich tych danych jednocześnie. To wielka szansa, ale i tak lepiej to sprawdzić.
źródło
Posługiwać się
zamiast
w tym przypadku.
źródło
Aby rozwiązać ten problem, po prostu wywołaj powiadomienieDataSetChanged () z pustą listą przed aktualizacją widoku kosza.
Na przykład
hcpArray.clear (); // Lista widoków przeglądu aktualizacji
źródło
W moim przypadku właśnie usunąłem linię z
setHasStableIds(true);
źródło
W moim przypadku próbowałem zmienić zawartość adaptera w wątku w tle, ale nazwałem powiadomienie * w wątku main / ui.
Nie jest możliwe! Powodem, dla którego powiadomienie jest zmuszane do głównego wątku, jest to, że widok recyklera chce, abyś edytował adapter kopii zapasowej w głównym wątku, nawet na tym samym stosie wywołań.
Aby rozwiązać problem, upewnij się, że każda operacja na twoim adapterze, a także każde powiadomienie ... jest wykonywane w wątku interfejsu użytkownika / głównym !
źródło
Niedawno natrafiłem na ten paskudny ślad stosu z nowymi komponentami architektury Androida. Zasadniczo mam listę elementów w moim ViewModel, które są obserwowane przez mój Fragment, używając LiveData. Gdy ViewModel publikuje nową wartość danych, Fragment aktualizuje adapter, przekazując te nowe elementy danych i powiadamiając adapter o zmianach.
Niestety, przekazując nowe elementy danych do adaptera, nie uwzględniłem faktu, że zarówno ViewModel, jak i Adapter wskazywałyby na to samo odwołanie do obiektu! Oznacza to, że jeśli zaktualizuję dane i zadzwonię
postValue()
z poziomu ViewModel, pojawi się bardzo małe okno, w którym dane mogą zostać zaktualizowane, a karta jeszcze nie powiadomiona!Moją poprawką było utworzenie nowej kopii elementów po przekazaniu do adaptera:
mList = new ArrayList<>(passedList);
Dzięki tej bardzo łatwej poprawce możesz mieć pewność, że dane adaptera nie zmienią się, dopóki nie zostanie powiadomiony o tym adapter.
źródło
To jest jedyne rozwiązanie, które działało dla mnie, nawet próbując wielu z powyższych rozwiązań.
1.) Intilizacja
2.) Napisz tę metodę w adapterze
stockListModels -> ta lista jest używana w adapterze.
źródło
Dla mnie zadziałało po dodaniu następującego wiersza kodu:
źródło
ten problem może wystąpić podczas próby wyczyszczenia listy, jeśli zamierzasz wyczyścić listę danych, zwłaszcza gdy używasz metody pull do odświeżenia, spróbuj użyć flagi boolean, zainicjuj ją jako false, a wewnątrz metody OnRefresh spraw, aby była prawdziwa, wyczyść swoją listę danych jeśli flaga jest prawdziwa tuż przed dodaniem do niej nowych danych, a następnie ustaw ją jako fałsz.
twój kod może być taki
źródło
Wcześniej miałem ten sam problem. W końcu znalazłem obejście tego problemu
Chcę powiadomić adapter, że element został usunięty, a następnie powiadomić o zmianie zestawu danych adaptera
źródło
Natknąłem się na podobny problem i właśnie go rozgryzłem. Na stałe zapisałem kilka przykładów dla przypadku testowego, ale nie upewniłem się, że każdy zwrócił unikalny identyfikator, co spowodowało dla mnie poniższą awarię. Naprawienie identyfikatorów rozwiązało problem, mam nadzieję, że pomoże to komuś innemu!
źródło
raz też dostałem błąd:
Przyczyna: Próbowałem zaktualizować widok Recycler View z zadania Async, jednocześnie próbując odzyskać stare usunięte viewHolders;
Kod: Generuję dane jednym naciśnięciem przycisku, logika jest następująca
Problem: Ilekroć przewijam szybko przed wygenerowaniem moich danych, dostaję
Rozwiązanie: zamiast wyczyścić RecyclerView przed wygenerowaniem moich danych, zamiast tego zostawiam go, a następnie zastępuję nowymi danymi, Call NotifyDatasetChanged, jak pokazano poniżej;
źródło
Po prostu usuń wszystkie widoki swojego menedżera układu przed powiadomieniem. lubić:
źródło
Korzystanie z
ListAdapter (androidx.recyclerview.widget.ListAdapter)
połączeniaadapter.submitList(null)
przed połączeniemadapter.submitList(list)
:źródło
Znalazłem to ustawienie mRecycler.setLayoutFrozen (true); w metodzie onRefresh swipeContainer.
rozwiązał problem dla mnie.
źródło
To dość paskudny błąd.
Aby obsłużyć kliknięcie elementu, użyłem implementacji
RecyclerView.OnItemTouchListener
podobnej do rozwiązania znalezionego w tym pytaniu .Po wielokrotnym odświeżeniu
RecyclerView
źródła danych i kliknięciu elementuIndexOutOfBoundsException
spowoduje to awarię mojej aplikacji. Po kliknięciu elementuRecyclerView
wewnętrzny szuka właściwego widoku podstawowego i przywraca mu pozycję. Sprawdzając kod źródłowy, zauważyłem, że było trochęTasks
iThreads
zaplanowane. Krótko mówiąc, jest to po prostu jakiś nielegalny stan, w którym dwa źródła danych są ze sobą połączone i nie są zsynchronizowane, a wszystko szaleje.Na tej podstawie Zdjąłem wdrożenie systemu
RecyclerView.OnItemTouchListener
i po prostu złapał kliknij naViewHolder
naAdapter
sobie:To może nie być najlepsze rozwiązanie, ale na razie bezproblemowe. Mam nadzieję, że pozwoli ci to zaoszczędzić trochę czasu :).
źródło
Lint dał mi radę dotyczącą niespójności: napisałem (onBindViewHolder ()):
który musiał zostać zastąpiony przez:
Uruchom oba kody w swoim kodzie, a następnie uruchom Lint, aby uzyskać pełne wyjaśnienie !!
źródło