Ich lade einige Daten aus dem Internet im Hintergrund-Thread herunter (ich verwende sie AsyncTask
) und zeige beim Herunterladen einen Fortschrittsdialog an. Die Ausrichtung ändert sich, die Aktivität wird neu gestartet und dann wird meine AsyncTask abgeschlossen. Ich möchte den Fortschrittsdialog schließen und eine neue Aktivität starten. Das Aufrufen von entlassenDialog löst jedoch manchmal eine Ausnahme aus (wahrscheinlich, weil die Aktivität zerstört wurde und eine neue Aktivität noch nicht gestartet wurde).
Was ist der beste Weg, um diese Art von Problem zu lösen (Aktualisieren der Benutzeroberfläche über einen Hintergrundthread, der auch dann funktioniert, wenn der Benutzer die Ausrichtung ändert)? Hat jemand von Google eine "offizielle Lösung" bereitgestellt?
Antworten:
Schritt 1: Machen Sie Ihre Klasse zu
AsyncTask
einerstatic
verschachtelten Klasse oder zu einer völlig separaten Klasse, nur nicht zu einer inneren (nicht statisch verschachtelten) Klasse.Schritt 2:
AsyncTask
Halten Sie dasActivity
Via über ein Datenelement fest, das über den Konstruktor und einen Setter festgelegt wurde.Schritt 3: Wenn Sie das erstellen
AsyncTask
, versorgen SieActivity
den Konstruktor mit Strom .Schritt 4: Geben Sie
onRetainNonConfigurationInstance()
die zurückAsyncTask
, nachdem Sie sie von der ursprünglichen Aktivität entfernt haben.Schritt 5:
onCreate()
Wenn diesgetLastNonConfigurationInstance()
nicht der Fall istnull
, übertragen Sie es in IhreAsyncTask
Klasse und rufen Sie Ihren Setter an, um Ihre neue Aktivität mit der Aufgabe zu verknüpfen.Schritt 6: Verweisen Sie nicht auf das Aktivitätsdatenmitglied von
doInBackground()
.Wenn Sie das obige Rezept befolgen, funktioniert alles.
onProgressUpdate()
undonPostExecute()
werden zwischen dem BeginnonRetainNonConfigurationInstance()
und dem Ende des folgenden suspendiertonCreate()
.Hier ist ein Beispielprojekt , das die Technik demonstriert.
Ein anderer Ansatz besteht darin
AsyncTask
, die Arbeit aufzugeben und in eine zu verschiebenIntentService
. Dies ist besonders nützlich, wenn die zu erledigende Arbeit langwierig sein kann und unabhängig davon fortgesetzt werden soll, was der Benutzer in Bezug auf Aktivitäten tut (z. B. Herunterladen einer großen Datei). Sie können eine geordnete Sendung verwendenIntent
, um die Aktivität entweder auf die ausgeführte Arbeit reagieren zu lassen (sofern sie noch im Vordergrund steht), oder eineNotification
, um den Benutzer darüber zu informieren, ob die Arbeit erledigt wurde. Hier ist ein Blog-Beitrag mit mehr zu diesem Muster.quelle
onRetainNonConfigurationInstance()
ist veraltet und die vorgeschlagene Alternative ist die VerwendungsetRetainInstance()
, gibt jedoch kein Objekt zurück. Ist es möglich,asyncTask
Konfigurationsänderungen mit zu bearbeitensetRetainInstance()
?Fragment
halten dieAsyncTask
. Haben Sie denFragment
AnrufsetRetainInstance(true)
auf sich. Habe dasAsyncTask
einzige Gespräch mit demFragment
. Bei einer Konfigurationsänderung wird dasFragment
nicht zerstört und neu erstellt (obwohl die Aktivität ausgeführt wird), sodass dasAsyncTask
während der Konfigurationsänderung beibehalten wird.Die akzeptierte Antwort war sehr hilfreich, hat aber keinen Fortschrittsdialog.
Zum Glück für Sie, Leser, habe ich ein äußerst umfassendes und funktionierendes Beispiel für eine AsyncTask mit einem Fortschrittsdialog erstellt !
quelle
Ich habe eine Woche lang gearbeitet, um eine Lösung für dieses Dilemma zu finden, ohne die Manifestdatei bearbeiten zu müssen. Die Annahmen für diese Lösung sind:
Implementierung
Sie müssen die beiden Dateien am Ende dieses Beitrags in Ihren Arbeitsbereich kopieren. Stellen Sie einfach sicher, dass:
Alle Ihre
Activity
s sollten sich verlängernBaseActivity
In
onCreate()
,super.onCreate()
sollten aufgerufen werden , nachdem Sie Mitglieder zu initialisieren , dass Bedarf von Ihrem zugegriffen werdenASyncTask
s. Überschreiben Sie außerdemgetContentViewId()
, um die Formularlayout-ID anzugeben.Überschreiben Sie
onCreateDialog()
wie gewohnt , um von der Aktivität verwaltete Dialoge zu erstellen.Im folgenden Code finden Sie ein Beispiel für eine statische innere Klasse zum Erstellen Ihrer AsyncTasks. Sie können Ihr Ergebnis in mResult speichern, um später darauf zugreifen zu können.
Und schließlich, um Ihre neue Aufgabe zu starten:
Das ist es! Ich hoffe, diese robuste Lösung wird jemandem helfen.
BaseActivity.java (Importe selbst organisieren)
SuperAsyncTask.java
quelle
Ja.
Die Lösung ist eher ein Vorschlag für eine Anwendungsarchitektur als nur ein Teil des Codes .
Sie schlugen drei Entwurfsmuster vor , mit denen eine Anwendung unabhängig vom Anwendungsstatus synchron mit einem Server arbeiten kann (dies funktioniert auch dann, wenn der Benutzer die App beendet, der Benutzer den Bildschirm ändert, die App beendet wird und jeder andere mögliche Status, in dem) eine Hintergrunddatenoperation könnte unterbrochen werden, dies deckt es ab)
Der Vorschlag wird in der Rede der Android REST-Clientanwendungen während der Google I / O 2010 von Virgil Dobjanschi erläutert. Es ist 1 Stunde lang, aber es ist sehr sehenswert.
Die Basis davon ist die Abstraktion von Netzwerkoperationen zu einem
Service
, der unabhängig von jedemActivity
in der Anwendung funktioniert . Wenn Sie mit Datenbanken arbeiten, erhalten Sie durch die Verwendung vonContentResolver
und erhaltenCursor
ein sofort einsatzbereites Observer-Muster , mit dem Sie die Benutzeroberfläche ohne zusätzliche Logik aktualisieren können, sobald Sie Ihre lokale Datenbank mit den abgerufenen Remote-Daten aktualisiert haben. Jeder andere Code nach der Operation würde über einen Rückruf ausgeführt, der an die übergeben wirdService
(ich verwende hierfür eineResultReceiver
Unterklasse).Wie auch immer, meine Erklärung ist eigentlich ziemlich vage, Sie sollten auf jeden Fall die Rede sehen.
quelle
Während Marks (CommonsWare) Antwort tatsächlich für Orientierungsänderungen funktioniert, schlägt sie fehl, wenn die Aktivität direkt zerstört wird (wie im Fall eines Telefonanrufs).
Sie können die Orientierungsänderungen UND die seltenen zerstörten Aktivitätsereignisse verarbeiten, indem Sie ein Anwendungsobjekt verwenden, um auf Ihre ASyncTask zu verweisen.
Es gibt eine ausgezeichnete Erklärung des Problems und die Lösung hier :
Ryan ist es zu verdanken, dass er das herausgefunden hat.
quelle
Nach 4 Jahren löste Google das Problem, indem er setRetainInstance (true) in Activity onCreate aufrief. Dadurch bleibt Ihre Aktivitätsinstanz während der Gerätedrehung erhalten. Ich habe auch eine einfache Lösung für ältere Android.
quelle
Sie sollten alle Aktivitätsaktionen mit dem Aktivitätshandler aufrufen. Wenn Sie sich in einem Thread befinden, sollten Sie eine ausführbare Datei erstellen und mit Activities Handler veröffentlichen. Andernfalls stürzt Ihre App manchmal mit schwerwiegenden Ausnahmen ab.
quelle
Dies ist meine Lösung: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
Grundsätzlich sind die Schritte:
onSaveInstanceState
speichere die Aufgabe, wenn sie noch bearbeitet wird.onCreate
bekomme ich die Aufgabe, wenn sie gespeichert wurde.onPause
verwerfe ich dasProgressDialog
wenn es angezeigt wird.onResume
zeige ich,ProgressDialog
ob die Aufgabe noch bearbeitet wird.quelle