Unsere Qualitätssicherung hat einen Fehler festgestellt: Beim Drehen des Android-Geräts (Droid Turbo) ist der folgende Absturz im Zusammenhang mit RecyclerView aufgetreten :
java.lang.IndexOutOfBoundsException: Inkonsistenz festgestellt. Ungültige Position 2 (Versatz: 2) .Zustand: 3
Für mich sieht es nach einem internen Fehler in RecyclerView aus, da ich mir keine Möglichkeit vorstellen kann, dass dies direkt durch unseren Code verursacht wird ...
Ist jemand auf dieses Problem gestoßen?
Was wäre die Lösung?
Eine brutale Problemumgehung könnte darin bestehen, die Ausnahme zu erfassen und die RecyclverView-Instanz von Grund auf neu zu erstellen, um zu vermeiden, dass ein beschädigter Status verbleibt.
Aber wenn möglich, möchte ich das Problem besser verstehen (und es vielleicht an der Quelle beheben), anstatt es zu maskieren.
Der Fehler ist nicht leicht zu reproduzieren, aber er ist fatal, wenn er auftritt.
Die vollständige Stapelverfolgung:
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
Antworten:
Ich hatte ein (möglicherweise) verwandtes Problem - das Eingeben einer neuen Instanz einer Aktivität mit einem RecyclerView, aber mit einem kleineren Adapter löste diesen Absturz für mich aus.
RecyclerView.dispatchLayout()
kann versuchen, Gegenstände aus dem Schrott zu ziehen, bevor Sie anrufenmRecycler.clearOldPositions()
. Die Folge ist, dass Elemente aus dem gemeinsamen Pool gezogen wurden, deren Positionen höher als die Adaptergröße waren.Glücklicherweise funktioniert dies nur, wenn
PredictiveAnimations
es aktiviert ist. Meine Lösung bestand also darin, eine Unterklasse zu erstellenGridLayoutManager
(LinearLayoutManager
hat das gleiche Problem und das gleiche Problem) und zu überschreibensupportsPredictiveItemAnimations()
, um false zurückzugeben:quelle
In meinem Fall (Daten in meine Datenstruktur löschen / einfügen) musste ich den Recycling-Pool löschen und dann den geänderten Datensatz benachrichtigen!
mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();
quelle
Verwenden Sie
notifyDataSetChanged()
stattdessennotifyItem...
in diesem Fall.quelle
notifyItem...
und korrigiert haben, der funktioniert, anstatt alle Elemente neu zu rendern.Ich löste dieses Problem, indem ich die
mRecycler.setAdapter(itemsAdapter)
Kasse verzögerte, nachdem ich alle Elemente zum Adapter hinzugefügt hatte,mRecycler.addAll(items)
und es funktionierte. Keine Ahnung, warum ich das zuerst getan habe. Aus dem Code einer Bibliothek habe ich diese Zeilen in der "falschen Reihenfolge" durchgesehen. Ich bin mir jedoch ziemlich sicher, dass dies der Fall ist. Bitte, wenn jemand dies bestätigen kann, erklären Sie, warum dies der Fall ist so? Ich bin mir nicht sicher, ob dies überhaupt eine gültige Antwort istquelle
swapAdapter(adapter, true)
stattdessen verwendetsetAdapter(adapter)
und es hat geholfen.Ich hatte ein ähnliches Problem, aber nicht genau das gleiche. In meinem Fall habe ich an einem Punkt das Array gelöscht, das an die Recycling-Ansicht übergeben wurde
und notifyDataSetChanged nicht aufrufen, da ich nicht wollte, dass die recyclerview die Ansichten sofort löscht. Ich habe das mObjects-Array in AsyncTask neu gefüllt.
quelle
Ich hatte das gleiche Problem mit recyclerView. Daher habe ich den Adapter direkt nach dem Löschen der Liste über die Änderung des Datensatzes informiert.
quelle
Ich habe das gleiche Problem. Es trat auf, als ich schnell scrollte und die API aufrief und Daten aktualisierte. Nachdem ich alles versucht hatte, um einen Absturz zu verhindern, fand ich eine Lösung.
Es wird klappen.
quelle
Ich ändere Daten für die
RecyclerView
im HintergrundThread
. Ich habe das gleicheException
wie das OP. Ich habe dies nach dem Ändern der Daten hinzugefügt:Ich hoffe es hilft
quelle
view.recycler_view.post
, habe ich verwendetnotifyItemInserted
. In meinem Fall war es bereits ein UI-Thread.Dieser Fehler tritt auf, wenn die Liste im Adapter beim Scrollen durch den Benutzer gelöscht wird, wodurch sich die Position des Artikelhalters ändert, die Referenz zwischen Liste und Artikel auf der Benutzeroberfläche verloren geht. Bei der nächsten Anforderung "notifyDataSetChanged" tritt ein Fehler auf .
Fix:
Überprüfen Sie Ihre Update-Listenmethode. Wenn du so etwas machst
Wie repariert man. Erstellen Sie ein neues Listenobjekt für die Pufferverarbeitung und weisen Sie es anschließend erneut der Hauptliste zu
Vielen Dank an Nhan Cao für diese tolle Hilfe :)
quelle
Mein Problem wurde behoben, nachdem ich meine
Adapter
Implementierung so geändert hatte , dass anstelle einer Referenz eine Kopie des Elements-Arrays verwendet wurde. DiesetItems()
Methode wird jedes Mal aufgerufen, wenn neue Elemente in der angezeigt werdenRecyclerView
.Anstatt:
Ich tat:
quelle
suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true);
und dies ist mein Konstruktorpublic CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Ich habe mit der gleichen Situation konfrontiert. Und es wurde durch Hinzufügen von Codes gelöst, bevor Sie Ihre Sammlung löschen.
mRecyclerView.getRecycledViewPool().clear();
quelle
In meinem Fall habe ich die Elemente aktualisiert und
notifyDataSetChanged
einen Nicht-UI-Thread aufgerufen. Die meiste Zeit hat es funktioniert, aber wenn viele Änderungen schnell vorgenommen wurden, stürzte es ab. Als ich es stattdessen tat, im Grundedann hörte es auf zu krachen.
quelle
Sie müssen nur Ihre Liste löschen
OnPostExecute()
und nicht währenddessenPull to Refresh
Ich habe festgestellt, dass dies passiert, wenn Sie während eines Pulls zum Aktualisieren scrollen , da ich die Liste vor dem
async task
gelöscht habe, was zujava.lang.IndexOutOfBoundsException: Inconsistency detected.
Auf diese Weise werden Sie nicht mit einer Inkonsistenz enden
quelle
Dies kann auch mit dem mehrmaligen gleichzeitigen Einstellen des Adapters zusammenhängen. Ich hatte eine Rückrufmethode, die 5-6 Mal gleichzeitig ausgelöst wurde, und ich stellte den Adapter in diesem Rückruf so ein, dass RecycledViewPool nicht mit all diesen Daten gleichzeitig umgehen konnte. Es ist eine fette Chance, aber du solltest es trotzdem besser ausprobieren.
quelle
Verwenden
stattdessen
in diesem Fall.
quelle
Um dieses Problem zu beheben, rufen Sie einfach notifyDataSetChanged () mit leerer Liste auf, bevor Sie die Recyclingansicht aktualisieren.
Beispielsweise
hcpArray.clear (); // Die Liste für die Update-Recycling-Ansicht
quelle
In meinem Fall habe ich gerade die Zeile mit entfernt
setHasStableIds(true);
quelle
In meinem Fall habe ich versucht, den Inhalt meines Adapters in einem Hintergrundthread zu ändern, aber im Haupt- / UI-Thread notify * aufgerufen.
Das ist nicht möglich! Der Grund, warum die Benachrichtigung zum Hauptthread gezwungen wird, besteht darin, dass die Recycling-Ansicht möchte, dass Sie Ihren Sicherungsadapter im Hauptthread bearbeiten, selbst auf demselben Aufrufstapel.
Um das Problem zu lösen, stellen Sie sicher, dass jeder Vorgang an Ihrem Adapter sowie jede Benachrichtigung ... über den UI / Main-Thread ausgeführt wird !
quelle
Ich bin kürzlich mit den neuen Android-Architekturkomponenten auf diesen fiesen Stack-Trace gestoßen. Im Wesentlichen habe ich eine Liste von Elementen in meinem ViewModel, die von meinem Fragment mithilfe von LiveData beobachtet werden. Wenn das ViewModel einen neuen Wert für die Daten veröffentlicht, aktualisiert das Fragment den Adapter, übergibt diese neuen Datenelemente und benachrichtigt den Adapter über Änderungen.
Leider konnte ich bei der Übergabe der neuen Datenelemente an den Adapter nicht berücksichtigen, dass sowohl das ViewModel als auch der Adapter auf dieselbe Objektreferenz verweisen würden! Das heißt, wenn ich die Daten aktualisiere und anrufe
postValue()
aus dem ViewModel heraus anrufe, gibt es ein sehr kleines Fenster, in dem die Daten aktualisiert und der Adapter noch nicht benachrichtigt werden kann!Mein Fix bestand darin, eine neue Kopie der Elemente zu instanziieren, wenn sie an den Adapter übergeben wurden:
mList = new ArrayList<>(passedList);
Mit dieser supereinfachen Lösung können Sie sicherstellen, dass sich Ihre Adapterdaten erst unmittelbar vor der Benachrichtigung Ihres Adapters ändern.
quelle
Dies ist nur eine Lösung, die für mich funktioniert hat, selbst wenn ich viele der oben genannten Lösungen ausprobiert habe.
1.) Intilisierung
2.) Schreiben Sie diese Methode in den Adapter
stockListModels -> Diese Liste verwenden Sie im Adapter.
quelle
Bei mir hat es nach dem Hinzufügen dieser Codezeile funktioniert:
quelle
Dieses Problem kann auftreten, wenn Sie versuchen, Ihre Liste zu löschen. Wenn Sie Ihre Datenliste löschen möchten, insbesondere wenn Sie Pull zum Aktualisieren verwenden, versuchen Sie, ein boolesches Flag zu verwenden, initialisieren Sie es als false und löschen Sie Ihre Datenliste innerhalb der OnRefresh-Methode auf true Wenn das Flag wahr ist, kurz bevor Sie die neuen Daten hinzufügen und danach falsch machen.
Ihr Code könnte so sein
quelle
Ich hatte vorher das gleiche Problem. Endlich eine Problemumgehung dafür gefunden
Was ich tue, ist, den Adapter zu benachrichtigen, dass das Element entfernt wurde, und dann den geänderten Bereich des Adapterdatensatzes zu benachrichtigen
quelle
Ich bin auf ein ähnliches Problem gestoßen und habe es gerade herausgefunden. Ich habe einige Beispiele für einen Testfall fest codiert, aber nicht sichergestellt, dass sie jeweils eine eindeutige ID zurückgeben. Dies hat den folgenden Absturz für mich verursacht. Durch das Beheben der IDs wurde das Problem behoben. Ich hoffe, dies hilft jemand anderem!
quelle
Ich habe auch einmal den Fehler bekommen:
Ursache: Ich habe versucht, eine Recycler-Ansicht über eine asynchrone Aufgabe zu aktualisieren, während gleichzeitig versucht wurde, alte gelöschte viewHolders abzurufen.
Code: Ich generiere Daten auf Knopfdruck, Logik wie folgt
Problem: Immer wenn ich schnell scrolle, bevor ich meine Daten generiere, bekomme ich
Lösung: Anstatt die RecyclerView vor dem Generieren meiner Daten zu löschen, lasse ich sie stattdessen und ersetze sie durch die neuen Daten, den Aufruf NotifyDatasetChanged, wie unten gezeigt.
quelle
Entfernen Sie einfach alle Ansichten Ihres Layout-Managers, bevor Sie benachrichtigt werden. mögen:
quelle
Mit
ListAdapter (androidx.recyclerview.widget.ListAdapter)
Anrufadapter.submitList(null)
vor dem Aufrufadapter.submitList(list)
:quelle
Ich fand diese Einstellung mRecycler.setLayoutFrozen (true); in der onRefresh-Methode des swipeContainer.
löste das Problem für mich.
quelle
Dies ist ein ziemlich böser Fehler.
Um meinen Artikelklick zu handhaben, habe ich eine Implementierung verwendet, die
RecyclerView.OnItemTouchListener
der in dieser Frage gefundenen Lösung ähnelt .Nach
RecyclerView
mehrmaligem Aktualisieren der Datenquelle und Klicken auf ein ElementIndexOutOfBoundsException
stürzte meine Anwendung ab. Wenn auf ein Element geklickt wird,RecyclerView
sucht das Element intern nach der richtigen zugrunde liegenden Ansicht und gibt die Position zurück. Beim Auschecken des Quellcodes sah ich, dass es einige gabTasks
undThreads
geplant war. Um es kurz zu machen, im Grunde ist es nur ein illegaler Zustand, in dem zwei Datenquellen vermischt und nicht synchronisiert werden und das Ganze wild wird.Auf dieser Grundlage entfernte ich meine Implementierung von
RecyclerView.OnItemTouchListener
und fing einfach den Klick auf dasViewHolder
vonAdapter
mir selbst auf:Dies ist möglicherweise nicht die beste Lösung, aber vorerst absturzfrei. Hoffentlich sparen Sie dadurch etwas Zeit :).
quelle
Lint gab mir einen Rat bezüglich Inkonsistenz: Ich schrieb (onBindViewHolder ()):
die ersetzt werden musste durch:
Führen Sie beide Codes in Ihrem Code aus und führen Sie dann Lint aus, um die vollständige Erklärung zu erhalten !!
quelle