Welchen Best-Practice-Weg gibt es, um die zugrunde liegenden Daten eines Adapters zu aktualisieren?

8

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.

    
skyler 25.04.2010, 06:23
quelle

2 Antworten

8
  

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 ?

zu implementieren?
  

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.

    
CommonsWare 25.04.2010, 11:37
quelle
0

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.

    
Yar 13.02.2011 19:00
quelle

Tags und Links