Ich stoße auf eine IllegalStateException, die eine zugrunde liegende Liste auf einen Adapter aktualisiert (möglicherweise ein ArrayAdapter oder eine Erweiterung von BaseAdapter, ich kann mich nicht erinnern). Ich habe den Text der Ausnahme im Moment nicht oder erinnere mich daran, aber er sagt etwas über den Inhalt der Liste aus, ohne dass der Adapter über die Änderung informiert wurde.
Diese Liste / darf von einem anderen Thread als dem UI-Thread (main) aktualisiert werden. Nach dem Aktualisieren dieser Liste (Hinzufügen eines Elements) rufe ich notifyDataSetChanged auf. Das Problem scheint zu sein, dass der Adapter oder ListView, der an den Adapter angeschlossen ist, versucht, sich selbst zu aktualisieren, bevor diese Methode aufgerufen wird. Wenn dies geschieht, wird die IllegalStateException ausgelöst.
Wenn ich die Sichtbarkeit der ListView vor dem Update auf GONE und dann wieder auf VISIBLE setze, tritt kein Fehler auf. Aber das ist nicht immer praktisch.
Ich habe irgendwo gelesen, dass Sie das zugrundeliegende nicht von einem anderen Thread ändern können - das scheint ein MVC-Muster zu begrenzen, da ich bei dieser bestimmten Liste Elemente aus verschiedenen Threads hinzufügen möchte. Ich nahm an, dass, solange ich notifyDataSetChanged () anrief, ich sicher wäre - dass der Adapter die zugrunde liegende Liste nicht erneut besuchte, bis diese Methode aufgerufen wurde, aber das scheint nicht der Fall zu sein.
Ich nehme an, was ich frage ist, kann es sicher sein, die zugrunde liegende Liste von anderen Threads als der Benutzeroberfläche zu aktualisieren? Wenn ich die Daten in einem Adapter ändern möchte, ändere ich zusätzlich die zugrunde liegende Liste oder den Adapter selbst (über seine add () - Methoden usw.). Das Ändern der Daten über den Adapter scheint falsch zu sein.
Ich stieß auf einen Thread auf einer anderen Website von jemandem, der ein ähnliches Problem wie ich zu haben scheint: Ссылка (das ist von wo ich die Visibility.GONE und .VISIBLE Idee packte).
Um Ihnen eine bessere Vorstellung von meinem speziellen Problem zu geben, beschreibe ich ein wenig, wie meine Liste, der Adapter usw. eingerichtet sind.
Ich habe ein Objekt namens Warteschlange, das eine LinkedList enthält. Die Warteschlange erweitert Observable, und wenn Dinge über die Methoden ihrer internen Liste hinzugefügt werden, rufe ich setChanged () und notifyListeners () auf. Dieses Queue-Objekt kann Elemente aus einer beliebigen Anzahl von Threads hinzufügen oder entfernen.
Ich habe eine einzelne "Warteschlangensicht" -Aktivität, die einen Adapter enthält. Diese Aktivität registriert in ihrer onCreate () - Methode einen Observer-Listener für mein Warteschlangenobjekt. In der update () Methode des Observers rufe ich notifyDataSetChanged () auf dem Adapter auf.
Ich habe eine Menge Log-Ausgaben hinzugefügt und festgestellt, dass bei diesem IllegalStateException-Ereignis mein Observer-Callback nie aufgerufen wurde. Es ist also so, als ob der Adapter die Änderung der Liste bemerkt hätte, bevor der Beobachter seine Beobachter benachrichtigt hätte, und rufe meine Methode auf, um den Adapter zu benachrichtigen, dass sich der Inhalt geändert hat.
Ich nehme also an, dass das eine gute Möglichkeit ist, einen Adapter zu riggen? Ist das ein Problem, weil ich den Inhalt des Adapters von einem anderen Thread als dem UI-Thread aktualisiere? Wenn dies der Fall ist, habe ich vielleicht eine Lösung im Sinn (geben Sie dem UI-Thread beim Erstellen das Handler-Queue-Objekt, und führen Sie alle List-Änderungen mit diesem Handler durch, aber das erscheint unsachgemäß).
Mir ist klar, dass dies ein sehr offener Beitrag ist, aber ich bin ein bisschen verloren und würde mich über Kommentare zu dem, was ich geschrieben habe, freuen.
Diese Liste / kann aktualisiert werden von ein anderer Thread als die Benutzeroberfläche Thread (Haupt)
Das wird nicht funktionieren.
Ich habe irgendwo gelesen, dass du nicht kannst modifiziere das zugrundeliegende aus ein anderer Thread - das würde scheinen Begrenzen Sie ein MVC-Muster, wie mit diesem bestimmte Liste, möchte ich Elemente hinzufügen aus verschiedenen Threads
MVC hat nichts mit Threads zu tun.
kann es sicher sein, das zu aktualisieren zugrunde liegende Liste von Threads anderen als die UI?
Nein. Andere Threads können Aktualisierungen für den Adapter auslösen (z. B. über post()
), aber die Aktualisierungen selbst müssen im Hauptanwendungs-Thread für einen Adapter verarbeitet werden, der derzeit an ein ListView
angehängt ist.
Zusätzlich, wenn ich das ändern möchte Daten innerhalb eines Adapters, ändere ich die zugrunde liegende Liste oder den Adapter selbst (über seine add (), etc. Methoden). Ändern der Daten über den Adapter scheint falsch.
Sie ändern Ihre Adapter
über die Adapter
selbst für ArrayAdapter
. Sie ändern Ihre Adapter
über die zugrunde liegende Datenbank / den Inhaltsanbieter für CursorAdapter
. Andere Adapter können variieren.
Ich habe ein Objekt namens Queue das enthält eine LinkedList. Die Warteschlange wird erweitert Beobachtbar und wenn Dinge hinzugefügt werden zu seiner internen Liste durch seine Methoden, rufe ich setChanged () und notifyListeners ().
Haben Sie in Betracht gezogen, LinkedBlockingQueue
zu verwenden, anstatt Ihre eigene Thread-sichere Queue
?
Diese Aktivität in ihrer onCreate () Methode registriert einen Observer-Listener zu meinem Queue-Objekt. In den Observer's update () -Methode rufe ich auf notifyDataSetChanged () auf dem Adapter.
Adapters
sollte notifyDataSetChanged()
für sich selbst aufrufen (wenn die Änderung von ihnen vorgenommen wird) oder von der Entität, die die Daten ändert (z. B. Cursor
für CursorAdapter
), von ihnen aufgerufen haben. Das ist MVC. Der Activity
sollte weder wissen noch sich darum kümmern, wenn sich das Datenmodell ändert.
Es ist also so, als ob der Adapter das bemerkt hätte Liste Änderung vor dem Observer hatte eine Chance, seine Beobachter zu benachrichtigen, und rufe meine Methode auf, um den Adapter zu benachrichtigen dass der Inhalt sich geändert hat.
Möglicherweise verwenden Sie ein ArrayAdapter
, in diesem Fall stört Sie all diese zusätzlichen Beobachter- / Benachrichtigungs-Sachen, da dies für Sie erledigt ist. Sie müssen nur dafür sorgen, dass ArrayAdapter
im Hauptanwendungs-Thread aktualisiert wird.
Ich nehme also an, was ich frage, ist, ist das ist eine gute Möglichkeit, einen Adapter zu riggen?
Nicht besonders, IMHO.
Ist das ein Problem, weil ich gerade aktualisiere? den Inhalt des Adapters aus einem Thread anders als der UI-Thread?
Wenn Sie die Updates nicht zurück zum Hauptanwendungs-Thread erzwingen, stürzt dieser schließlich ab, sobald Sie Ihre anderen Probleme geklärt haben.
geben Sie dem Warteschlangenobjekt einen Handler an UI-Thread, wenn es erstellt wird, und machen Alle Listenänderungen, die das verwenden Handler, aber das scheint unsachgemäß
Sie könnten ein Handler
verwenden, oder Sie könnten post()
auf Ihrem angehängten ListView
aufrufen.
Aus der Hand, würde ich eine Unterklasse von ArrayAdapter
namens ThreadSafeArrayAdapter
erstellen und sie anstelle von Queue
verwenden. ThreadSafeArrayAdapter
ersetzt add()
, insert()
und remove()
durch solche, bei denen die Oberklasse im Hauptanwendungs-Thread über Handler
oder post()
eine Sache macht.
Ein guter Rat ist Ссылка
Persönlich verwende ich meinen benutzerdefinierten Thread (eine Klasse, die den Thread erweitert), aber über eine Nachricht eine Antwort an den Benutzeroberflächenthread. Also in der Funktion run () des Threads gibt es:
%Vor%Der mExtHandler wurde der externen Handler-Instanz im Thread-Konstruktor zugewiesen. Der UI-Thread definiert den Nachrichtenhandler:
%Vor%Das ist eigentlich einfacher als AsyncTask.
Tags und Links android