Mein Programm führt einige Netzwerkaktivitäten in einem Hintergrundthread durch. Vor dem Start wird ein Fortschrittsdialog angezeigt. Der Dialog wird auf dem Handler geschlossen. Dies alles funktioniert einwandfrei, außer wenn sich die Bildschirmausrichtung ändert, während der Dialog geöffnet ist (und der Hintergrund-Thread ausgeführt wird). Zu diesem Zeitpunkt stürzt die App entweder ab oder blockiert sie oder gerät in eine seltsame Phase, in der die App erst funktioniert, wenn alle Threads beendet wurden.
Wie kann ich mit der Änderung der Bildschirmausrichtung ordnungsgemäß umgehen?
Der folgende Beispielcode entspricht in etwa dem, was mein reales Programm tut:
public class MyAct extends Activity implements Runnable {
public ProgressDialog mProgress;
// UI has a button that when pressed calls send
public void send() {
mProgress = ProgressDialog.show(this, "Please wait",
"Please wait",
true, true);
Thread thread = new Thread(this);
thread.start();
}
public void run() {
Thread.sleep(10000);
Message msg = new Message();
mHandler.sendMessage(msg);
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mProgress.dismiss();
}
};
}
Stapel:
E/WindowManager( 244): Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager( 244): android.view.WindowLeaked: Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager( 244): at android.view.ViewRoot.<init>(ViewRoot.java:178)
E/WindowManager( 244): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:147)
E/WindowManager( 244): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:90)
E/WindowManager( 244): at android.view.Window$LocalWindowManager.addView(Window.java:393)
E/WindowManager( 244): at android.app.Dialog.show(Dialog.java:212)
E/WindowManager( 244): at android.app.ProgressDialog.show(ProgressDialog.java:103)
E/WindowManager( 244): at android.app.ProgressDialog.show(ProgressDialog.java:91)
E/WindowManager( 244): at MyAct.send(MyAct.java:294)
E/WindowManager( 244): at MyAct$4.onClick(MyAct.java:174)
E/WindowManager( 244): at android.view.View.performClick(View.java:2129)
E/WindowManager( 244): at android.view.View.onTouchEvent(View.java:3543)
E/WindowManager( 244): at android.widget.TextView.onTouchEvent(TextView.java:4664)
E/WindowManager( 244): at android.view.View.dispatchTouchEvent(View.java:3198)
Ich habe versucht, den Fortschrittsdialog in onSaveInstanceState zu schließen, aber das verhindert nur einen sofortigen Absturz. Der Hintergrund-Thread läuft noch und die Benutzeroberfläche befindet sich in einem teilweise gezeichneten Zustand. Sie müssen die gesamte App beenden, bevor sie wieder funktioniert.
quelle
Antworten:
Wenn Sie die Ausrichtung ändern, erstellt Android eine neue Ansicht. Wahrscheinlich kommt es zu Abstürzen, weil Ihr Hintergrund-Thread versucht, den Status des alten zu ändern. (Möglicherweise liegt auch ein Problem vor, da sich Ihr Hintergrund-Thread nicht im UI-Thread befindet.)
Ich würde vorschlagen, diesen mHandler flüchtig zu machen und ihn zu aktualisieren, wenn sich die Ausrichtung ändert.
quelle
Bearbeiten: Google-Ingenieure empfehlen diesen Ansatz nicht, wie von Dianne Hackborn (auch bekannt als hackbod ) in diesem StackOverflow-Beitrag beschrieben . Weitere Informationen finden Sie in diesem Blogbeitrag .
Sie müssen dies zur Aktivitätsdeklaration im Manifest hinzufügen:
so sieht es aus
Die Sache ist, dass das System die Aktivität zerstört, wenn eine Änderung in der Konfiguration auftritt. Siehe ConfigurationChanges .
Wenn Sie dies in die Konfigurationsdatei einfügen, kann das System Ihre Aktivitäten nicht zerstören. Stattdessen wird die
onConfigurationChanged(Configuration)
Methode aufgerufen .quelle
orientation
gibt es noch viele weitere Gründe für eine Änderung der Konfiguration:keyboardHidden
(Ich habe die Wiki-Antwort bereits bearbeitet.)uiMode
(Zum Beispiel das Ein- und Aussteigen aus dem Automodus; Ändern des Nachtmodus) usw. Ich frage mich jetzt, ob dies tatsächlich der Fall ist eine gute Antwort.Ich habe eine solide Lösung für diese Probleme gefunden, die dem „Android Way“ der Dinge entspricht. Ich habe alle meine lang laufenden Operationen mit dem IntentService-Muster.
Das heißt, meine Aktivitäten senden Absichten, der IntentService erledigt die Arbeit, speichert die Daten in der Datenbank und sendet dann klebrige Absichten. Der klebrige Teil ist wichtig, sodass wir auch dann antworten und die Daten aus der aufrufenden Aktivität abrufen können, wenn die Aktivität während der Zeit unterbrochen wurde, nachdem der Benutzer die Arbeit initiiert hat und die Echtzeitübertragung vom IntentService verpasst hat.
ProgressDialog
s kann mit diesem Muster ganz gut arbeitenonSaveInstanceState()
.Grundsätzlich müssen Sie ein Flag speichern, für das ein Fortschrittsdialog im gespeicherten Instanzpaket ausgeführt wird. Unterlassen SieSpeichern Sie das Fortschrittsdialogobjekt , da dadurch die gesamte Aktivität verloren geht. Um das Fortschrittsdialogfeld dauerhaft zu behandeln, speichere ich es als schwache Referenz im Anwendungsobjekt. Bei einer Orientierungsänderung oder etwas anderem, das dazu führt, dass die Aktivität angehalten wird (Telefonanruf, Benutzer trifft nach Hause usw.) und dann fortgesetzt wird, schließe ich den alten Dialog aus und erstelle einen neuen Dialog in der neu erstellten Aktivität.
Für unbestimmte Fortschrittsdialoge ist dies einfach. Für den Fortschrittsbalkenstil müssen Sie den letzten bekannten Fortschritt in das Bundle und alle Informationen, die Sie lokal in der Aktivität verwenden, einfügen, um den Fortschritt zu verfolgen. Wenn Sie den Fortschritt wiederherstellen, verwenden Sie diese Informationen, um den Fortschrittsbalken im selben Status wie zuvor erneut zu erzeugen und dann basierend auf dem aktuellen Status zu aktualisieren.
Zusammenfassend lässt sich sagen,
onSaveInstanceState()
dass Sie durch das Einfügen lang laufender Aufgaben in einen IntentService in Verbindung mit einem umsichtigen Einsatz von Dialoge effizient verfolgen und dann über die gesamten Lebenszyklusereignisse der Aktivität hinweg wiederherstellen können. Relevante Teile des Aktivitätscodes sind unten aufgeführt. Sie benötigen auch Logik in Ihrem BroadcastReceiver, um Sticky-Absichten angemessen zu behandeln, aber das geht über den Rahmen hinaus.quelle
Ich habe das gleiche Problem getroffen. Meine Aktivität muss einige Daten von einer URL analysieren und ist langsam. Dazu erstelle ich einen Thread und zeige dann einen Fortschrittsdialog. Ich lasse den Thread eine Nachricht zurück an den UI-Thread senden,
Handler
wenn er fertig ist. InHandler.handleMessage
bekomme ich das Datenobjekt (jetzt fertig) vom Thread und fülle es in die Benutzeroberfläche. Es ist also Ihrem Beispiel sehr ähnlich.Nach vielen Versuchen und Irrtümern habe ich anscheinend eine Lösung gefunden. Zumindest kann ich jetzt den Bildschirm jederzeit drehen, bevor oder nachdem der Thread fertig ist. In allen Tests wird der Dialog ordnungsgemäß geschlossen und alle Verhaltensweisen sind wie erwartet.
Was ich getan habe, ist unten gezeigt. Das Ziel ist es, mein Datenmodell (
mDataObject
) zu füllen und es dann in die Benutzeroberfläche einzufügen. Sollte jederzeit ohne Überraschung eine Bildschirmdrehung ermöglichen.Das funktioniert bei mir. Ich weiß nicht, ob dies die "richtige" Methode ist, wie sie von Android entwickelt wurde - sie behaupten, dass diese "Aktivität während der Bildschirmdrehung zerstören / neu erstellen" die Dinge tatsächlich einfacher macht, also denke ich, dass es nicht zu schwierig sein sollte.
Lassen Sie mich wissen, wenn Sie ein Problem in meinem Code sehen. Wie oben gesagt, weiß ich nicht wirklich, ob es irgendwelche Nebenwirkungen gibt.
quelle
onRetainNonConfigurationInstance()
undgetLastNonConfigurationInstance()
half mir, mein Problem zu lösen. Daumen hoch!Das ursprünglich wahrgenommene Problem war, dass der Code eine Änderung der Bildschirmausrichtung nicht überleben würde. Anscheinend wurde dies "gelöst", indem das Programm die Änderung der Bildschirmausrichtung selbst handhabte, anstatt das UI-Framework dies tun zu lassen (über den Aufruf von onDestroy).
Ich würde sagen, wenn das zugrunde liegende Problem darin besteht, dass das Programm auf Destroy () nicht überlebt, ist die akzeptierte Lösung nur eine Problemumgehung, die das Programm mit ernsthaften anderen Problemen und Schwachstellen belastet. Denken Sie daran, dass das Android-Framework ausdrücklich angibt, dass Ihre Aktivität aufgrund von Umständen, auf die Sie keinen Einfluss haben, fast jederzeit zerstört werden kann. Daher muss Ihre Aktivität in der Lage sein, onDestroy () und nachfolgendes onCreate () aus irgendeinem Grund zu überleben, nicht nur durch eine Änderung der Bildschirmausrichtung.
Wenn Sie Änderungen an der Bildschirmausrichtung selbst akzeptieren möchten, um das Problem des OP zu lösen, müssen Sie sicherstellen, dass andere Ursachen von onDestroy () nicht zu demselben Fehler führen. Schaffst du das? Wenn nicht, würde ich fragen, ob die "akzeptierte" Antwort wirklich sehr gut ist.
quelle
Meine Lösung bestand darin, die
ProgressDialog
Klasse zu erweitern, um meine eigene zu bekommenMyProgressDialog
.Ich habe
show()
unddismiss()
Methoden neu definiert , um die Ausrichtung zu sperren, bevor ich sie zeige,Dialog
und sie wieder zu entsperren, wenn sieDialog
entlassen wird. Wenn also dasDialog
angezeigt wird und sich die Ausrichtung des Geräts ändert, bleibt die Ausrichtung des Bildschirms bisdismiss()
zum Aufruf erhalten, und die Bildschirmausrichtung ändert sich entsprechend den Sensorwerten / Geräteorientierung.Hier ist mein Code:
quelle
Ich hatte das gleiche Problem und habe eine Lösung gefunden, die nicht mit dem ProgressDialog funktioniert, und ich erhalte schnellere Ergebnisse.
Ich habe ein Layout erstellt, das eine ProgressBar enthält.
Führen Sie dann in der onCreate-Methode die folgenden Schritte aus
Führen Sie dann die lange Aufgabe in einem Thread aus. Wenn dies abgeschlossen ist, lassen Sie Runnable die Inhaltsansicht auf das tatsächliche Layout einstellen, das Sie für diese Aktivität verwenden möchten.
Zum Beispiel:
Dies ist, was ich getan habe, und ich habe festgestellt, dass es schneller läuft als das Anzeigen des ProgressDialogs und es ist weniger aufdringlich und sieht meiner Meinung nach besser aus.
Wenn Sie jedoch den ProgressDialog verwenden möchten, ist diese Antwort nichts für Sie.
quelle
setContentView(R.layout.my_layout);
ist nicht ausreichend; Sie müssen alle Listener einstellen, Daten zurücksetzen usw.Ich habe eine Lösung dafür gefunden, die ich anderswo noch nicht gesehen habe. Sie können ein benutzerdefiniertes Anwendungsobjekt verwenden, das weiß, ob Hintergrundaufgaben ausgeführt werden, anstatt dies in der Aktivität zu versuchen, die bei einer Änderung der Ausrichtung zerstört und neu erstellt wird. Ich gebloggt dies hier .
quelle
Application
wird normalerweise verwendet, um einen globalen Anwendungsstatus beizubehalten. Ich sage nicht, dass es nicht funktioniert, aber es scheint zu kompliziert. Aus dem Dokument "Es ist normalerweise nicht erforderlich, Anwendung in Unterklassen zu unterteilen.". Ich bevorzuge weitgehend die Antwort von sonxurxo.Ich werde meinen Ansatz zur Behandlung dieses Rotationsproblems beitragen. Dies ist möglicherweise nicht relevant für OP, da er es nicht verwendet
AsyncTask
, aber vielleicht finden es andere nützlich. Es ist ziemlich einfach, aber es scheint den Job für mich zu erledigen:Ich habe eine Anmeldeaktivität mit einer verschachtelten
AsyncTask
Klasse namensBackgroundLoginTask
.In meinem
BackgroundLoginTask
Fall mache ich nichts Außergewöhnliches, außer beim Abrufen des Anrufs einen Null-Check hinzuzufügenProgressDialog
:Dies dient dazu, den Fall zu behandeln, in dem die Hintergrundaufgabe beendet wird, während die
Activity
nicht sichtbar ist, und daher der Fortschrittsdialog von deronPause()
Methode bereits geschlossen wurde.Als Nächstes
Activity
erstelle ich in meiner übergeordneten Klasse globale statische Handles für meineAsyncTask
Klasse und meineProgressDialog
(dieAsyncTask
verschachtelten können auf diese Variablen zugreifen):Dies dient zwei Zwecken: Erstens ermöglicht es mir
Activity
,AsyncTask
auch von einer neuen, nachgedrehten Aktivität aus immer auf das Objekt zuzugreifen . Zweitens ermöglicht es mir,BackgroundLoginTask
auf die zuzugreifen und sie zu schließenProgressDialog
auch nach einer Drehung .Als nächstes füge ich dies hinzu
onPause()
, wodurch der Fortschrittsdialog verschwindet, wenn wirActivity
den Vordergrund verlassen (wodurch dieser hässliche Absturz beim "Schließen erzwingen" verhindert wird):Schließlich habe ich Folgendes in meiner
onResume()
Methode:Dadurch kann das
Dialog
wieder angezeigt werden, nachdem dasActivity
neu erstellt wurde.Hier ist die gesamte Klasse:
Ich bin kein erfahrener Android-Entwickler, also zögern Sie nicht zu kommentieren.
quelle
Verschieben Sie die lange Aufgabe in eine separate Klasse. Implementieren Sie es als Subjekt-Beobachter-Muster. Immer wenn die Aktivität erstellt wird, registrieren Sie sich und schließen Sie die Registrierung mit der Taskklasse. Die Taskklasse kann AsyncTask verwenden.
quelle
Der Trick besteht darin, den Dialog in AsyncTask während onPreExecute / onPostExecute wie gewohnt anzuzeigen / zu schließen. Bei einer Änderung der Ausrichtung wird jedoch eine neue Instanz des Dialogs in der Aktivität erstellt / angezeigt und der Verweis auf die Aufgabe übergeben.
quelle
Ich habe es so gemacht:
Sie können auch versuchen, mich wissen zu lassen, ob es für Sie funktioniert oder nicht
quelle
Wenn Sie einen Hintergrund erstellen
Service
, der das ganze schwere Heben erledigt (TCP-Anforderungen / Antwort, Unmarshalling), kann dasView
undActivity
zerstört und neu erstellt werden, ohne dass Fenster verloren gehen oder Daten verloren gehen. Dies ermöglicht das von Android empfohlene Verhalten, bei dem eine Aktivität bei jeder Konfigurationsänderung zerstört wird (z. B. bei jeder Orientierungsänderung).Es ist etwas komplexer, aber es ist der beste Weg, um Serveranfragen, Daten vor / nach der Verarbeitung usw. aufzurufen.
Sie können sogar Ihre verwenden
Service
um jede Anforderung an einen Server in die Warteschlange zu stellen, sodass die Bearbeitung dieser Aufgaben einfach und effizient ist.Der Dev Guide enthält ein vollständiges Kapitel über
Services
.quelle
Service
ist mehr Arbeit als aAsyncTask
, kann aber in manchen Situationen ein besserer Ansatz sein. Es ist nicht unbedingt besser, oder? Davon abgesehen verstehe ich nicht, wie dies das Problem löst,ProgressDialog
dass aus der Hauptleitung durchgesickert istActivity
. Wo instanziieren Sie dasProgressDialog
? Wo entlassen Sie es?Ich habe eine Implementierung, mit der die Aktivität bei einer Änderung der Bildschirmausrichtung zerstört werden kann, der Dialog in der neu erstellten Aktivität jedoch weiterhin erfolgreich zerstört wird. ich benutze
...NonConfigurationInstance
, um die Hintergrundaufgabe an die neu erstellte Aktivität anzuhängen. Das normale Android-Framework übernimmt die Neuerstellung des Dialogs selbst, dort wird nichts geändert.Ich habe AsyncTask in eine Unterklasse unterteilt und ein Feld für die Aktivität "Besitzen" sowie eine Methode zum Aktualisieren dieses Besitzers hinzugefügt.
In meiner Aktivitätsklasse habe ich ein Feld hinzugefügt,
backgroundTask
das auf die "eigene" Hintergrundaufgabe verweist, und ich aktualisiere dieses Feld mitonRetainNonConfigurationInstance
undgetLastNonConfigurationInstance
.Verbesserungsvorschläge:
backgroundTask
Referenz in der Aktivität, nachdem die Aufgabe beendet wurde, um Speicher oder andere damit verbundene Ressourcen freizugeben.ownerActivity
Referenz in der Hintergrundaufgabe, bevor die Aktivität zerstört wird, falls sie nicht sofort neu erstellt wird.BackgroundTask
Schnittstelle und / oder Sammlung, damit verschiedene Arten von Aufgaben von derselben Eigentümeraktivität ausgeführt werden können.quelle
Wenn Sie zwei Layouts beibehalten, sollte der gesamte UI-Thread beendet werden.
Wenn Sie AsynTask verwenden, können Sie die
.cancel()
Methode innerhalb deronDestroy()
Methode der aktuellen Aktivität problemlos aufrufen .Weitere Informationen zu AsyncTask finden Sie im Abschnitt "Abbrechen einer Aufgabe" hier .
Update: Bedingung zur Überprüfung des Status hinzugefügt, da diese nur abgebrochen werden kann, wenn sie sich im laufenden Zustand befindet. Beachten Sie auch, dass die AsyncTask nur einmal ausgeführt werden kann.
quelle
Versucht, die Lösung von jfelectron zu implementieren , weil es sich um eine " Lösung für diese Probleme handelt, die der" Android- entspricht. Es dauerte jedoch einige Zeit, bis alle genannten Elemente und zusammengestellt wurden. Am Ende war diese etwas andere, und ich denke elegantere Lösung hier in ihrer Gesamtheit veröffentlicht.
Verwendet einen IntentService, der von einer Aktivität ausgelöst wurde, um die lange laufende Aufgabe in einem separaten Thread auszuführen. Der Dienst gibt klebrige Broadcast-Absichten an die Aktivität zurück, die den Dialog aktualisiert. Die Aktivität verwendet showDialog (), onCreateDialog () und onPrepareDialog (), um zu vermeiden, dass persistente Daten im Anwendungsobjekt oder im Paket "savedInstanceState" übergeben werden müssen. Dies sollte unabhängig davon funktionieren, wie Ihre Anwendung unterbrochen wird.
Aktivitätsklasse:
IntentService-Klasse:
Manifest-Dateieinträge:
vor dem Bewerbungsabschnitt:
innerhalb des Anwendungsbereichs
quelle
Dies ist meine vorgeschlagene Lösung:
void showProgressDialog()
diesem Zweck kann der Fragment-Activity-Listener-Schnittstelle eine Methode hinzugefügt werden.quelle
Ich sah mich der gleichen Situation gegenüber. Ich habe nur eine Instanz für meinen Fortschrittsdialog in der gesamten Anwendung erhalten.
Zuerst habe ich eine DialogSingleton-Klasse erstellt, um nur eine Instanz zu erhalten (Singleton-Muster).
Wie ich in dieser Klasse zeige, habe ich den Fortschrittsdialog als Attribut. Jedes Mal, wenn ich einen Fortschrittsdialog anzeigen muss, erhalte ich die eindeutige Instanz und erstelle einen neuen Fortschrittsdialog.
Wenn ich mit der Hintergrundaufgabe fertig bin, rufe ich die eindeutige Instanz erneut auf und schließe ihren Dialog.
Ich speichere den Status der Hintergrundaufgabe in meinen freigegebenen Einstellungen. Wenn ich den Bildschirm drehe, frage ich, ob für diese Aktivität eine Aufgabe ausgeführt wird: (onCreate)
Wenn ich eine Hintergrundaufgabe starte:
Wenn ich mit dem Ausführen einer Hintergrundaufgabe fertig bin:
Ich hoffe, es hilft.
quelle
Dies ist eine sehr alte Frage, die aus irgendeinem Grund in der Seitenleiste auftauchte.
Wenn die Hintergrundaufgabe nur überleben muss, während sich die Aktivität im Vordergrund befindet, besteht die "neue" Lösung darin, den Hintergrundthread (oder vorzugsweise
AsyncTask
) in einem beibehaltenen Fragment zu hosten , wie in diesem Entwicklerhandbuch und zahlreichen Fragen und Antworten beschrieben .Ein beibehaltenes Fragment bleibt erhalten, wenn die Aktivität für eine Konfigurationsänderung zerstört wird, jedoch nicht, wenn die Aktivität im Hintergrund oder im Backstack zerstört wird. Daher sollte die Hintergrundaufgabe immer noch unterbrochen werden, wenn sie
isChangingConfigurations()
falsch istonPause()
.quelle
Ich bin ein Neuling in Android und ich habe es versucht und es hat funktioniert.
quelle
Ich habe ALLES versucht. Verbrachte Tage damit zu experimentieren. Ich wollte nicht verhindern, dass sich die Aktivität dreht. Mein Szenario war:
Das Problem war, dass beim Drehen des Bildschirms jede Lösung im Buch fehlschlug. Selbst mit der AsyncTask-Klasse, die die richtige Android-Methode ist, um mit diesen Situationen umzugehen. Beim Drehen des Bildschirms ist der aktuelle Kontext, mit dem der Startthread arbeitet, verschwunden und der angezeigte Dialog wird durcheinander gebracht. Das Problem war immer der Dialog, egal wie viele Tricks ich dem Code hinzugefügt habe (Übergeben neuer Kontexte an laufende Threads, Beibehalten von Thread-Zuständen durch Rotationen usw.). Die Codekomplexität am Ende war immer riesig und es gab immer etwas, das schief gehen konnte.
Die einzige Lösung, die für mich funktioniert hat, war der Aktivitäts- / Dialogtrick. Es ist einfach und genial und alles rotationssicher:
Anstatt einen Dialog zu erstellen und ihn anzuzeigen, erstellen Sie eine Aktivität, die im Manifest mit android: theme = "@ android: style / Theme.Dialog" festgelegt wurde. Es sieht also nur wie ein Dialog aus.
Ersetzen Sie showDialog (DIALOG_ID) durch startActivityForResult (yourActivityDialog, yourCode).
Verwenden Sie onActivityResult in der aufrufenden Aktivität, um die Ergebnisse des ausführenden Threads (auch die Fehler) abzurufen und die Benutzeroberfläche zu aktualisieren.
Verwenden Sie in Ihrem 'ActivityDialog' Threads oder AsyncTask, um lange Aufgaben auszuführen, und onRetainNonConfigurationInstance, um den Status "dialog" beim Drehen des Bildschirms zu speichern.
Das ist schnell und funktioniert gut. Ich verwende immer noch Dialoge für andere Aufgaben und die AsyncTask für etwas, das keinen konstanten Dialog auf dem Bildschirm erfordert. Aber in diesem Szenario gehe ich immer zum Aktivitäts- / Dialogmuster.
Ich habe es nicht ausprobiert, aber es ist sogar möglich, das Drehen dieser Aktivität / dieses Dialogfelds zu blockieren, wenn der Thread ausgeführt wird, wodurch die Dinge beschleunigt werden und die aufrufende Aktivität gedreht werden kann.
quelle
Intent
, die restriktiver sind als die istObject
durch erlaubtAsyncTask
Heutzutage gibt es eine viel klarere Möglichkeit, mit solchen Problemen umzugehen. Der typische Ansatz ist:
1. Stellen Sie sicher, dass Ihre Daten ordnungsgemäß von der Benutzeroberfläche getrennt sind:
Alles, was ein Hintergrundprozess ist, sollte gespeichert bleiben
Fragment
(setzen Sie dies mitFragment.setRetainInstance()
. Dies wird zu Ihrem 'dauerhaften Datenspeicher', in dem alle Daten gespeichert werden, die Sie behalten möchten. Nach dem Ereignis der Orientierungsänderung ist dasFragment
Original weiterhin verfügbar Status durch einenFragmentManager.findFragmentByTag()
Anruf (wenn Sie es erstellen, sollten Sie ihm ein Tag geben, keine ID, da es nicht an a angehängt istView
).Siehe die Handhabung Runtime Änderungen entwickelten Reiseführer für Informationen über diese richtig zu tun und warum es die beste Option.
2. Stellen Sie sicher, dass Sie eine korrekte und sichere Schnittstelle zwischen den Hintergrundprozessen und Ihrer Benutzeroberfläche herstellen:
Sie müssen Ihren Verknüpfungsprozess umkehren . Im Moment hängt sich Ihr Hintergrundprozess an a an
View
- stattdessenView
sollten Sie sich an den Hintergrundprozess anhängen. Es macht mehr Sinn, oder? DieView
Aktion des ist abhängig vom Hintergrundprozess, während der Hintergrundprozess nicht vom Hintergrundprozess abhängt.View
Dies bedeutet, dass die Verknüpfung zu einer Standardschnittstelle geändertListener
wird. Sagen Sie Ihren Prozess (was auch immer Klasse ist es - ob es eine istAsyncTask
,Runnable
oder was auch immer) definiert einOnProcessFinishedListener
, wenn der Prozess es nennen soll , dass die Zuhörer gemacht wird , wenn es vorhanden ist .Diese Antwort ist eine schöne, kurze Beschreibung, wie man benutzerdefinierte Listener erstellt.
3. Verknüpfen Sie Ihre Benutzeroberfläche bei jeder Erstellung der Benutzeroberfläche mit dem Datenprozess (einschließlich Änderungen der Ausrichtung):
Jetzt müssen Sie sich darum kümmern, die Hintergrundaufgabe mit Ihrer aktuellen
View
Struktur zu verbinden. Wenn Sie Ihre Orientierungsänderungen richtig handhaben (nicht dieconfigChanges
Hack-Leute, die immer empfohlen werden), werden SieDialog
vom System neu erstellt. Dies ist wichtig. Dies bedeutet, dass bei der Orientierungsänderung alleDialog
Lebenszyklusmethoden Ihres Systems abgerufen werden. Bei jeder dieser Methoden (onCreateDialog
normalerweise ein guter Ort) können Sie einen Aufruf wie den folgenden ausführen:Im Fragment-Lebenszyklus können Sie entscheiden, wo die Einstellung des Listeners am besten zu Ihrer individuellen Implementierung passt.
Dies ist ein allgemeiner Ansatz, um eine robuste und vollständige Lösung für das in dieser Frage gestellte generische Problem bereitzustellen. Abhängig von Ihrem individuellen Szenario fehlen in dieser Antwort wahrscheinlich einige kleinere Teile. Dies ist jedoch im Allgemeinen der korrekteste Ansatz, um Orientierungsänderungsereignisse richtig zu behandeln.
quelle
Ich habe eine einfachere Lösung gefunden, um mit Fäden umzugehen, wenn sich die Ausrichtung ändert. Sie können einfach einen statischen Verweis auf Ihre Aktivität / Ihr Fragment behalten und überprüfen, ob er null ist, bevor Sie auf die Benutzeroberfläche einwirken. Ich schlage vor, auch einen Versuch zu verwenden:
quelle
Wenn Sie Probleme haben , Orientierungsänderungsereignisse eines Dialogfelds zu erkennen, UNABHÄNGIG VON EINER AKTIVITÄTSREFERENZ , funktioniert diese Methode aufregend gut. Ich verwende dies, weil ich eine eigene Dialogklasse habe, die in mehreren verschiedenen Aktivitäten angezeigt werden kann, sodass ich nicht immer weiß, in welcher Aktivität sie angezeigt wird. Mit dieser Methode müssen Sie das AndroidManifest nicht ändern. Sorgen Sie sich um Aktivitätsreferenzen. und Sie brauchen keinen benutzerdefinierten Dialog (wie ich). Sie benötigen jedoch eine benutzerdefinierte Inhaltsansicht, damit Sie die Orientierungsänderungen mithilfe dieser bestimmten Ansicht erkennen können. Hier ist mein Beispiel:
Installieren
Implementierung 1 - Dialog
Implementierung 2 - AlertDialog.Builder
Implementierung 3 - ProgressDialog / AlertDialog
quelle
Dies ist meine Lösung, als ich damit konfrontiert wurde: Es
ProgressDialog
ist kein untergeordnetesFragment
Element, daher kann meine benutzerdefinierte Klasse "ProgressDialogFragment
"DialogFragment
stattdessen erweitert werden, um den für Konfigurationsänderungen angezeigten Dialog beizubehalten.Es gibt zwei Lösungsansätze:
Erster Ansatz: Führen Sie die Aktivität aus, die den Dialog verwendet, um den Status während der Konfigurationsänderung in der Manifestdatei beizubehalten:
Dieser Ansatz wird von Google nicht bevorzugt.
Zweiter Ansatz: Bei der
onCreate()
Methode der Aktivität müssen Sie Ihre beibehalten,DialogFragment
indem Sie dieProgressDialogFragment
mit dem Titel und der Nachricht wie folgt neu erstellen, wenn diesavedInstanceState
nicht null ist:quelle
Scheint viel zu "schnell und schmutzig" zu sein, um wahr zu sein. Bitte weisen Sie auf die Mängel hin, aber ich fand, dass es funktioniert hat ...
Innerhalb der onPostExecute-Methode meiner AsyncTask habe ich einfach die '.dismiss' für den Fortschrittsdialog in einen try / catch-Block (mit einem leeren catch) eingeschlossen und dann die ausgelöste Ausnahme einfach ignoriert. Scheint falsch zu sein, aber es scheint, dass es keine negativen Auswirkungen gibt (zumindest für das, was ich später mache, nämlich eine andere Aktivität zu starten, die als Ergebnis meiner langjährigen Abfrage als Extra übergeben wird).
quelle
Activity
hat der einen Verweis auf dieDialog
. Wenn die Konfiguration geändert wird, wird die ersteActivity
zerstört, was bedeutet, dass alle Felder auf gesetzt sindnull
. Das Low-LevelWindowManager
hat aber auch einen Hinweis auf dasDialog
(da es noch nicht entlassen wurde). Das neueActivity
versucht, ein neuesDialog
(inpreExecute()
) zu erstellen, und der Fenstermanager löst eine schwerwiegende Ausnahme aus, die Sie daran hindert. In der Tat, wenn es dies tut, würde es keine Möglichkeit geben, das zu zerstören,Dialog
wodurch ein Verweis auf die Initiale beibehalten wirdActivity
. Habe ich recht?Die einfachste und flexibelste Lösung ist die Verwendung einer AsyncTask mit einem statischen Verweis auf ProgressBar . Dies bietet eine gekapselte und somit wiederverwendbare Lösung für Orientierungsänderungsprobleme. Diese Lösung hat mir gute Dienste für verschiedene asynchrone Aufgaben geleistet, einschließlich Internet-Downloads, Kommunikation mit Diensten und Dateisystem-Scans. Die Lösung wurde auf mehreren Android-Versionen und Telefonmodellen gut getestet. Eine vollständige Demo finden Sie hier mit besonderem Interesse an DownloadFile.java
Ich präsentiere das Folgende als Konzeptbeispiel
Die Verwendung in einer Android-Aktivität ist einfach
quelle
Wenn Sie die Ausrichtung ändern, beendet Android diese Aktivität und erstellt eine neue Aktivität. Ich empfehle die Nachrüstung mit Rx Java. welcher Griff stürzt automatisch ab.
Verwenden Sie diese Methode beim Nachrüsten.
.subscribeOn (Schedulers.io ()) .observeOn (AndroidSchedulers.mainThread ())
quelle