Was ich tun möchte : Führen Sie einen Hintergrund-Thread aus, der den ListView-Inhalt berechnet und ListView teilweise aktualisiert, während die Ergebnisse berechnet werden.
Was ich weiß, muss ich vermeiden : Ich kann mich nicht mit ListAdapter-Inhalten aus dem Hintergrund-Thread anlegen, daher habe ich AsyncTask geerbt und das Ergebnis (Einträge zum Adapter hinzufügen) von onProgressUpdate veröffentlicht. Mein Adapter verwendet ArrayList von Ergebnisobjekten. Alle Vorgänge auf diesen Arraylisten werden synchronisiert.
Forschung anderer Leute : Es gibt sehr wertvolle Daten hier . Ich litt auch unter fast täglichen Abstürzen für eine Gruppe von ~ 500 Benutzern, und als ich list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)
Block in onProgressUpdate hinzufügte , verringerten sich die Abstürze um den Faktor 10, verschwanden aber nicht. (es wurde als Antwort vorgeschlagen )
Was ich manchmal bekam : Bitte beachten Sie, dass es sehr selten vorkommt (einmal pro Woche für einen von 3.5k Benutzern). Aber ich möchte diesen Fehler vollständig beseitigen. Hier ist eine teilweise Stapelverfolgung:
`java.lang.IllegalStateException:` The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter(class com.transportoid.Tracks.TrackListAdapter)]
at android.widget.ListView.layoutChildren(ListView.java:1432)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:2062)
at android.widget.ListView.onTouchEvent(ListView.java:3234)
at android.view.View.dispatchTouchEvent(View.java:3709)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:852)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
[...]
Hilfe? Wird nicht mehr benötigt, siehe unten
ENDGÜLTIGE ANTWORT: Wie sich herausstellte, habe ich notifyDataSetChanged
alle 5 Einfügungen aufgerufen, um ein Flackern und plötzliche Listenänderungen zu vermeiden. Dies ist nicht möglich. Benachrichtigen Sie den Adapter immer, wenn sich die Basisliste ändert. Dieser Fehler ist für mich jetzt schon lange vorbei.
Antworten:
Ich hatte das gleiche Problem.
Ich habe Elemente
ArrayList
außerhalb des UI-Threads hinzugefügt .Lösung: Ich habe beides getan
adding the items
undnotifyDataSetChanged()
den UI-Thread aufgerufen .quelle
Ich hatte das gleiche Problem, aber ich habe es mit der Methode behoben
aus der Klasse
ListView
quelle
Dies ist ein MultiThreading- Problem und die Verwendung ordnungsgemäß synchronisierter Blöcke. Dies kann verhindert werden. Ohne zusätzliche Dinge auf den UI-Thread zu setzen und die Reaktionsfähigkeit der App zu beeinträchtigen.
Ich stand auch vor dem gleichen. Und wie die am meisten akzeptierte Antwort vorschlägt, kann das Problem durch Ändern der Adapterdaten von UI Thread behoben werden. Das wird funktionieren, ist aber eine schnelle und einfache Lösung, aber nicht die beste.
Wie Sie für einen Normalfall sehen können. Das Aktualisieren des Datenadapters vom Hintergrundthread und das Aufrufen von notifyDataSetChanged im UI-Thread funktioniert.
Diese illegalStateException tritt auf, wenn ein UI-Thread die Ansicht aktualisiert und ein anderer Hintergrund-Thread die Daten erneut ändert. Dieser Moment verursacht dieses Problem.
Wenn Sie also den gesamten Code synchronisieren, der die Adapterdaten ändert und notifydatasetchange aufruft. Dieses Problem sollte weg sein. Da für mich weg und ich aktualisiere immer noch die Daten aus dem Hintergrund-Thread.
Hier ist mein fallspezifischer Code, auf den andere verweisen können.
Mein Loader auf dem Hauptbildschirm lädt die Telefonbuchkontakte im Hintergrund in meine Datenquellen.
Dieser PhoneBookManager.getPhoneBookContacts liest Kontakte aus dem Telefonbuch und füllt sie in die Hashmaps aus. Dies ist direkt für Listenadapter zum Zeichnen von Listen verwendbar.
Auf meinem Bildschirm befindet sich eine Schaltfläche. Das öffnet eine Aktivität, in der diese Telefonnummern aufgelistet sind. Wenn ichAdapter direkt über die Liste setze, bevor der vorherige Thread seine Arbeit beendet, kommt es schnell vor, dass die Navigation seltener vorkommt. Es wird die Ausnahme angezeigt. Dies ist der Titel dieser SO-Frage. Also muss ich so etwas in der zweiten Aktivität machen.
Mein Loader in der zweiten Aktivität wartet auf den Abschluss des ersten Threads. Bis es einen Fortschrittsbalken anzeigt. Überprüfen Sie den loadInBackground beider Lader.
Dann erstellt es den Adapter und liefert ihn an die Aktivität, bei der ich auf dem UI-Thread setAdapter aufrufe.
Das hat mein Problem gelöst.
Dieser Code ist nur ein Ausschnitt. Sie müssen es ändern, um gut für Sie zu kompilieren.
Hoffe das hilft
quelle
Ich habe das gelöst, indem ich 2 Listen habe. Eine Liste verwende ich nur für den Adapter und führe alle Datenänderungen / -aktualisierungen in der anderen Liste durch. Auf diese Weise kann ich Aktualisierungen für eine Liste in einem Hintergrundthread durchführen und anschließend die "Adapter" -Liste im Haupt- / UI-Thread aktualisieren:
quelle
Ich habe diesen Code geschrieben und ihn ~ 12 Stunden lang in einem 2.1-Emulator-Image laufen lassen und die IllegalStateException nicht erhalten. Ich werde dem Android-Framework den Vorteil des Zweifels an diesem geben und sagen, dass es höchstwahrscheinlich ein Fehler in Ihrem Code ist. Ich hoffe das hilft. Vielleicht können Sie es an Ihre Liste und Daten anpassen.
quelle
Vor einigen Tagen bin ich auf dasselbe Problem gestoßen und verursache mehrere tausend Abstürze pro Tag. Etwa 0,1% der Benutzer treffen diese Situation. Ich habe es versucht
setVisibility(GONE/VISIBLE)
undrequestLayout()
, aber die Anzahl der Abstürze nimmt nur geringfügig ab.Und ich habe es endlich gelöst. Nichts mit
setVisibility(GONE/VISIBLE)
. Nichts mitrequestLayout()
.Schließlich fand ich den Grund, warum ich a verwendet habe
Handler
, umnotifyDataSetChanged()
nach der Aktualisierung der Daten aufzurufen , was zu einer Art führen kann:checkForTap()
/onTouchEvent()
und schließlich anruftlayoutChildren()
)notifyDataSetChanged()
Ansichten auf und aktualisiert sieUnd ich machte einen weiteren Fehler , dass in
getCount()
,getItem()
undgetView()
ich verwenden , um Felder in Datasource direkt, anstatt Kopie sie an den Adapter. Also stürzt es schließlich ab, wenn:getCount()
undgetView()
wird aufgerufen, und listview stellt fest, dass Daten nicht konsistent sind, und löst Ausnahmen wie ausjava.lang.IllegalStateException: The content of the adapter has changed but...
. Eine weitere häufige Ausnahme ist dieIndexOutOfBoundException
Verwendung von Kopf- / Fußzeilen inListView
.Die Lösung ist also einfach. Ich kopiere nur Daten von meiner DataSource auf den Adapter, wenn mein Handler den Adapter auslöst, um Daten und Anrufe abzurufen
notifyDataSetChanged()
. Der Absturz passiert jetzt nie wieder.quelle
Wenn dies zeitweise passiert, stellte sich heraus, dass ich dieses Problem nur hatte, als die Liste gescrollt wurde, nachdem auf das letzte Element "Mehr laden" geklickt wurde. Wenn die Liste nicht gescrollt wurde, funktionierte alles einwandfrei.
Nach VIELEM Debugging war es ein Fehler von meiner Seite, aber auch eine Inkonsistenz im Android-Code.
Wenn die Validierung erfolgt, wird dieser Code in ListView ausgeführt
Wenn onChange auftritt, wird dieser Code in AdapterView (übergeordnetes Element von ListView) ausgelöst.
Beachten Sie, dass der Adapter NICHT garantiert derselbe ist!
In meinem Fall habe ich, da es sich um einen 'LoadMoreAdapter' handelte, den WrappedAdapter im getAdapter-Aufruf zurückgegeben (für den Zugriff auf die zugrunde liegenden Objekte). Dies führte dazu, dass die Anzahl aufgrund des zusätzlichen Elements "Mehr laden" und der ausgelösten Ausnahme unterschiedlich war.
Ich habe das nur gemacht, weil die Dokumente den Anschein erwecken, dass es in Ordnung ist
ListView.getAdapter javadoc
quelle
Mein Problem betraf die Verwendung eines Filters zusammen mit der ListView.
Beim Festlegen oder Aktualisieren des zugrunde liegenden Datenmodells von ListView habe ich Folgendes getan:
Das Aufrufen
filter()
der letzten Zeile führt (und muss) dazunotifyDataSetChanged()
, dass die Filtermethode aufgerufenpublishResults()
wird. Dies kann manchmal in Ordnung sein, insbesondere in meinem schnellen Nexus 5. In Wirklichkeit verbirgt es jedoch einen Fehler, den Sie bei langsameren Geräten oder unter ressourcenintensiven Bedingungen bemerken werden.Das Problem besteht darin, dass die Filterung asynchron erfolgt und somit zwischen dem Ende der
filter()
Anweisung und dem Aufruf vonpublishResults()
beiden anderen UI-Thread-Codes im UI-Thread möglicherweise den Inhalt des Adapters ausgeführt und geändert wird.Die eigentliche Korrektur ist einfach. Rufen Sie einfach an,
notifyDataSetChanged()
bevor Sie die Filterung anfordern:quelle
Ich habe eine Liste, wenn Feed-Objekte. Es wird an einen Thread ohne Benutzeroberfläche angehängt und abgeschnitten. Es funktioniert gut mit Adapter unten. Ich rufe
FeedAdapter.notifyDataSetChanged
trotzdem UI-Thread auf, aber etwas später. Ich mag das, weil meine Feed-Objekte im lokalen Dienst im Speicher bleiben, auch wenn die Benutzeroberfläche tot ist.quelle
Ich hatte das gleiche Problem mit genau dem gleichen Fehlerprotokoll. In meinem Fall
onProgress()
fügt AsyncTask die Werte mit dem Adapter hinzumAdapter.add(newEntry)
. Um zu vermeiden, dass die Benutzeroberfläche weniger reagiert, setze ich 4-mal pro SekundemAdapter.setNotifyOnChange(false)
und rufe anmAdapter.notifyDataSetChanged()
. Einmal pro Sekunde wird das Array sortiert.Dies funktioniert gut und macht sehr süchtig, aber leider ist es möglich, es durch häufiges Berühren der angezeigten Listenelemente zum Absturz zu bringen.
Aber anscheinend habe ich eine akzeptable Problemumgehung gefunden. Ich vermute, dass der Adapter, selbst wenn Sie nur am UI-Thread arbeiten, nicht viele Änderungen an seinen Daten akzeptiert, ohne ihn aufzurufen
notifyDataSetChanged()
. Aus diesem Grund habe ich eine Warteschlange erstellt, in der alle neuen Elemente gespeichert werden, bis die genannten 300 ms abgelaufen sind. Wenn dieser Moment erreicht ist, füge ich alle gespeicherten Gegenstände auf einmal hinzu und rufe annotifyDataSetChanged()
. Bis jetzt konnte ich die Liste nicht mehr zum Absturz bringen .quelle
Dies ist ein bekannter Fehler in Android 4 bis 4.4 (KitKat) und wird in "> 4.4" behoben.
Siehe hier: https://code.google.com/p/android/issues/detail?id=71936
quelle
Selbst wenn ich in meiner XMPP-Benachrichtigungsanwendung auf dasselbe Problem gestoßen bin, muss die Empfängernachricht wieder zur Listenansicht hinzugefügt werden (implementiert mit
ArrayList
). Als ich versuchte, den Empfängerinhalt überMessageListener
(separater Thread) hinzuzufügen , wurde die Anwendung mit dem obigen Fehler beendet. Ich habe dieses Problem gelöst, indem ich den Inhalt zu meinerarraylist
&setListviewadapater
through-runOnUiThread
Methode hinzugefügt habe, die Teil der Aktivitätsklasse ist. Dies löste mein Problem.quelle
Ich hatte ein ähnliches Problem. So habe ich es in meinem Fall gelöst. Ich überprüfe, ob dies
task
bereits der Fall istRUNNING
oder obFINISHED
eine Aufgabe nur einmal ausgeführt werden kann. Unten sehen Sie einen teilweisen und angepassten Code aus meiner Lösung.quelle
Ich hatte das gleiche Problem und habe es gelöst. Mein Problem war, dass ich einen
listview
mit einem Array-Adapter und mit Filter verwendete. Bei der Methode habeperformFiltering
ich mit dem Array herumgespielt, das die Daten enthält, und es war das Problem, da diese Methode nicht auf dem UI-Thread ausgeführt wird und EVENTUELL einige Probleme aufwirft.quelle
Eine Ursache für diesen Absturz ist, dass sich das
ArrayList
Objekt nicht vollständig ändern kann. Wenn ich also einen Artikel entferne, muss ich Folgendes tun:Dies hat den Absturz für mich behoben.
quelle
In meinem Fall habe ich die Methode
GetFilter()
auf einem Adapter aus derTextWatcher()
Methode in der Hauptaktivität aufgerufen und die Daten mit einer For-Schleife hinzugefügtGetFilter()
. Die Lösung bestand darin, die For-Schleife in derAfterTextChanged()
Hauptaktivität in die Submethode zu ändern und den Aufruf von zu löschenGetFilter()
quelle
quelle
Ich habe auch genau den gleichen Fehler erhalten und AsyncTask verwendet:
Ich habe es gelöst, indem ich
adapter.notifyDataSetChanged();
am Ende meines UI-Threads meine AsyncTask onPostExecute-Methode eingefügt habe. So was :Jetzt funktioniert meine App.
BEARBEITEN: Tatsächlich stürzte meine App immer noch etwa alle 1 von 10 Mal ab und gab den gleichen Fehler aus.
Irgendwann bin ich
runOnUiThread
auf einen früheren Beitrag gestoßen, den ich für nützlich hielt. Also habe ich es wie folgt in meine doInBackground-Methode eingefügt:Und ich habe die
adapter.notifyDataSetChanged();
Methode entfernt. Jetzt stürzt meine App nie mehr ab.quelle
Bitte versuchen Sie eine dieser Lösungen:
Wenn Sie der Datenliste in einem Thread (oder einer
doInBackground
Methode) ein neues Objekt hinzufügen , tritt dieser Fehler manchmal auf. Die Lösung lautet: Erstellen Sie eine temporäre Liste und fügen Sie dieser Liste im Thread (oderdoInBackground
) Daten hinzu. Kopieren Sie dann alle Daten aus der temporären Liste in die Liste der Adapter im UI-Thread (oderonPostExcute
).Stellen Sie sicher, dass alle UI-Updates im UI-Thread aufgerufen werden.
quelle
Ich hatte das gleiche Problem beim Hinzufügen neuer Daten in Lazy Image Loader, die ich gerade gestellt habe
im
hoffe es hilft dir
quelle
Wie @Mullins sagte "
Ich habe beide die Elemente hinzugefügt und
notifyDataSetChanged()
den UI-Thread aufgerufen und dies behoben. - Mullins".In meinem Fall habe ich
asynctask
und ich habenotifyDataSetChanged()
diedoInBackground()
Methode aufgerufen und das Problem ist gelöst, als ich vononPostExecute()
anrief, erhielt ich die Ausnahme.quelle
Ich hatte einen Brauch
ListAdapter
und riefsuper.notifyDataSetChanged()
am Anfang und nicht am Ende der Methode aufquelle
Ich hatte die gleiche Situation, ich hatte viele Buttongroups, die meinen Artikel in die Listenansicht aufgenommen haben, und ich habe einige boolesche Werte in meinem Artikel geändert, wie z. B. Inhaber.rbVar.setOnclik ...
Mein Problem trat auf, weil ich eine Methode in getView () aufrief. und speicherte ein Objekt in sharepreference, so dass ich oben den gleichen Fehler hatte
Wie ich es gelöst habe; Ich habe meine Methode in getView () entfernt, umDataSetInvalidated () zu benachrichtigen, und das Problem ist behoben
quelle
Ich hatte das gleiche Problem. Endlich habe ich die Lösung
Wenn die Softtastatur vorhanden ist, schließen Sie sie zuerst, bevor Sie die Listenansicht aktualisieren. Danach Datenquelle einstellen und notifydatasetchanged () aufrufen.
Beim internen Schließen der Tastatur aktualisiert listview die Benutzeroberfläche. Es ruft weiter an, bis die Tastatur geschlossen wird. Wenn sich die Datenquelle ändert, wird diese Ausnahme ausgelöst. Wenn Daten in onActivityResult aktualisiert werden, besteht die Möglichkeit eines gleichen Fehlers.
quelle
Meine Lösung:
1) Erstellen Sie eine
temp ArrayList
.2) erledigen Sie Ihre schweren Arbeiten (SQLite Row Fetch, ...) in
doInBackground
Führen Methode aus und fügen Sie der temporären Arrayliste Elemente hinzu.3) Fügen Sie alle Elemente aus der temporären Liste in die Arrayliste Ihrer Listenansicht ein
onPostExecute
.note:
Möglicherweise möchten Sie einige Elemente aus der Listenansicht und auch aus der SQLite-Datenbank löschen und möglicherweise einige Dateien löschen, die sich auf Elemente von der SD-Karte beziehen. Entfernen Sie einfach Elemente aus der Datenbank, entfernen Sie die zugehörigen Dateien und fügen Sie sie der temporären Arrayliste hinzubackground thread
.UI thread
Löschen Sie dann in temporäre Arrayliste vorhandene Elemente aus der Arrayliste der Listenansicht.Hoffe das hilft.
quelle