Ich erhalte Benutzerberichte von meiner App auf dem Markt und erhalte die folgende Ausnahme:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
Anscheinend hat es etwas mit einem FragmentManager zu tun, den ich nicht benutze. Der Stacktrace zeigt keine meiner eigenen Klassen an, daher habe ich keine Ahnung, wo diese Ausnahme auftritt und wie ich sie verhindern kann.
Für die Aufzeichnung: Ich habe einen Tabhost, und in jedem Tab gibt es eine ActivityGroup, die zwischen Aktivitäten wechselt.
android
android-fragments
android-viewpager
illegalstateexception
fragmenttransaction
Nhaarman
quelle
quelle
FragmentManager
, ist Honeycomb sicherlich. Passiert das auf echten Honeycomb-Tabletten? Oder könnte es sein, dass jemand eine gehackte Wabe auf einem Telefon oder so etwas ausführt und es diese gehackte Edition ist, die Schwierigkeiten hat?Antworten:
Bitte überprüfen Sie meine Antwort hier . Grundsätzlich musste ich nur:
Rufen Sie
super()
diesaveInstanceState
Methode nicht auf. Das hat die Dinge durcheinander gebracht ...Dies ist ein bekannter Fehler im Support-Paket.
Wenn Sie die Instanz speichern und etwas zu Ihrer hinzufügen müssen, können
outState
Bundle
Sie Folgendes verwenden:Am Ende war die richtige Lösung (wie in den Kommentaren zu sehen) zu verwenden:
beim Hinzufügen oder Ausführen des
FragmentTransaction
, das das verursachteException
.quelle
popBackStackImmediate
schlägt sie sofort fehl, wenn der Status gespeichert wurde. Das vorherige Hinzufügen des Fragments mitcommitAllowingStateLoss
spielt keine Rolle. Meine Tests zeigen, dass dies wahr ist. Dies hat keine Auswirkungen auf diese spezielle Ausnahme. Was wir brauchen, ist einepopBackStackImmediateAllowingStateLoss
Methode.Es gibt viele verwandte Probleme mit einer ähnlichen Fehlermeldung. Überprüfen Sie die zweite Zeile dieser bestimmten Stapelverfolgung. Diese Ausnahme bezieht sich speziell auf den Aufruf von
FragmentManagerImpl.popBackStackImmediate
.Dieser Methodenaufruf, wie
popBackStack
, wird immer mit fehlschlagen ,IllegalStateException
wenn der Sitzungszustand bereits gespeichert wurde. Überprüfen Sie die Quelle. Sie können nichts tun, um das Auslösen dieser Ausnahme zu verhindern.super.onSaveInstanceState
hilft nicht.commitAllowingStateLoss
hilft nicht.So habe ich das Problem beobachtet:
onSaveInstanceState
wird aufgerufen.popBackStackImmediate
versucht.IllegalStateException
ist geworfen.Folgendes habe ich getan, um es zu lösen:
Da es nicht möglich ist,
IllegalStateException
den Rückruf zu vermeiden , fangen und ignorieren Sie ihn.Dies reicht aus, um den Absturz der App zu verhindern. Aber jetzt wird der Benutzer die App wiederherstellen und sehen, dass die Schaltfläche, die er gedrückt zu haben glaubte, überhaupt nicht gedrückt wurde (sie denken). Das Formularfragment wird noch angezeigt!
Um dies zu beheben, geben Sie beim Erstellen des Dialogfelds einen Status an, um anzuzeigen, dass der Prozess gestartet wurde.
Und speichern Sie diesen Status im Bundle.
Vergessen Sie nicht, es wieder in zu laden
onViewCreated
Wenn Sie fortfahren, setzen Sie die Fragmente zurück, wenn zuvor versucht wurde, sie zu senden. Dies verhindert, dass der Benutzer zu einem scheinbar nicht übermittelten Formular zurückkehrt.
quelle
popBackStackImmediate
von Android selbst aufgerufen wurde?Überprüfen Sie die Aktivität,
isFinishing()
bevor Sie das Fragment anzeigen, und achten Sie daraufcommitAllowingStateLoss()
.Beispiel:
quelle
DialogFragment
. Siehe stackoverflow.com/questions/15729138/... für andere gute Lösungen, stackoverflow.com/a/41813953/2914140 mir geholfen.Es ist Oktober 2017 und Google erstellt die Android Support Library mit der neuen Lifecycle-Komponente. Es enthält eine neue Idee für das Problem "Diese Aktion kann nach onSaveInstanceState nicht ausgeführt werden".
Zusamenfassend:
Längere Version mit erklären:
Warum kommt dieses Problem heraus?
Dies liegt daran, dass Sie versuchen,
FragmentManager
aus Ihrer Aktivität (die Ihr Fragment enthalten soll, nehme ich an?) Eine Transaktion für Ihr Fragment festzuschreiben. Normalerweise sieht es so aus, als würden Sie versuchen, eine Transaktion für ein bevorstehendes Fragment durchzuführen, während die Host-Aktivität bereits diesavedInstanceState
Methode aufruft (der Benutzer kann zufällig die Home-Taste berühren, damit die Aktivität aufruftonStop()
, in meinem Fall ist dies der Grund).Normalerweise sollte dieses Problem nicht auftreten - wir versuchen immer, das Fragment ganz am Anfang in die Aktivität zu laden, da die
onCreate()
Methode ein perfekter Ort dafür ist. Manchmal passiert dies jedoch , insbesondere wenn Sie nicht entscheiden können, welches Fragment Sie in diese Aktivität laden möchten, oder wenn Sie versuchen, ein Fragment aus einemAsyncTask
Block zu laden (oder etwas etwas Zeit in Anspruch nimmt). Die Zeit, bevor die Fragmenttransaktion tatsächlich stattfindet, aber nach deronCreate()
Methode der Aktivität kann der Benutzer alles tun. Wenn der Benutzer die Home-Taste drückt, wodurch dieonSavedInstanceState()
Methode der Aktivitätcan not perform this action
ausgelöst wird , kommt es zu einem Absturz.Wenn jemand mehr über diese Ausgabe erfahren möchte, empfehle ich ihm, sich diesen Blog- Beitrag anzusehen . Es schaut tief in die Quellcodeschicht hinein und erklärt viel darüber. Es gibt auch den Grund, warum Sie die
commitAllowingStateLoss()
Methode nicht verwenden sollten, um diesen Absturz zu umgehen (glauben Sie mir, sie bietet nichts Gutes für Ihren Code).Wie kann ich das beheben?
Soll ich die
commitAllowingStateLoss()
Methode zum Laden des Fragments verwenden? Nein, solltest du nicht ;Sollte ich die
onSaveInstanceState
Methode überschreiben , die darin enthaltenesuper
Methode ignorieren ? Nein, solltest du nicht ;Sollte ich die magische
isFinishing
Insider-Aktivität verwenden, um zu überprüfen, ob die Host-Aktivität zum richtigen Zeitpunkt für eine Fragmenttransaktion ist? Ja, das scheint der richtige Weg zu sein.Schauen Sie sich an, welcher Lebenszyklus Komponente leisten kann.
Grundsätzlich führt Google eine Implementierung innerhalb der
AppCompatActivity
Klasse durch (und mehrere andere Basisklassen, die Sie in Ihrem Projekt verwenden sollten), wodurch es einfacher wird, den aktuellen Lebenszyklusstatus zu bestimmen . Werfen Sie einen Blick zurück auf unser Problem: Warum sollte dieses Problem auftreten? Das liegt daran, dass wir etwas zum falschen Zeitpunkt tun. Also versuchen wir es nicht zu tun und dieses Problem wird verschwunden sein.Ich codiere ein wenig für mein eigenes Projekt, hier ist was ich benutze
LifeCycle
. Ich codiere in Kotlin.Wie ich oben zeige. Ich werde den Lebenszyklusstatus der Hostaktivität überprüfen. Mit der Lifecycle-Komponente in der Support-Bibliothek könnte dies spezifischer sein. Der Code
lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)
bedeutet, wenn der aktuelle Status mindestens istonResume
, nicht später als er? Dies stellt sicher, dass meine Methode während eines anderen Lebenszustands (wieonStop
) nicht ausgeführt wird.Ist alles erledigt?
Natürlich nicht. Der Code, den ich gezeigt habe, zeigt einen neuen Weg, um zu verhindern, dass die Anwendung abstürzt. Aber wenn es in den Zustand von geht
onStop
, wird diese Codezeile keine Dinge tun und somit nichts auf Ihrem Bildschirm anzeigen. Wenn Benutzer zur Anwendung zurückkehren, wird ein leerer Bildschirm angezeigt. Dies ist die leere Hostaktivität, bei der überhaupt keine Fragmente angezeigt werden. Es ist eine schlechte Erfahrung (ja, ein bisschen besser als ein Absturz).Hier wünschte ich mir, es könnte etwas Schöneres geben: Die App stürzt nicht ab, wenn es später um den Lebenszustand geht
onResume
Die Transaktionsmethode ist dem Lebenszustand bewusst. Außerdem wird die Aktivität versuchen, diese Fragmenttransaktionsaktion fortzusetzen, nachdem der Benutzer zu unserer App zurückgekehrt ist.Ich füge dieser Methode noch etwas hinzu:
Ich pflege eine Liste innerhalb dieser
dispatcher
Klasse, um diese Fragmente zu speichern, habe ich keine Chance, die Transaktionsaktion abzuschließen. Wenn der Benutzer vom Startbildschirm zurückkommt und feststellt, dass noch ein Fragment darauf wartet, gestartet zu werden, wird dieresume()
Methode unter der@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
Anmerkung aufgerufen. Jetzt denke ich, dass es so funktionieren sollte, wie ich es erwartet hatte.quelle
FragmentDispatcher
eine Liste zum Speichern ausstehender Fragmente, wenn nur ein Fragment wiederhergestellt wird?Hier ist eine andere Lösung für dieses Problem.
Mit einer privaten Mitgliedsvariablen können Sie die zurückgegebenen Daten als Absicht festlegen, die dann nach super.onResume () verarbeitet werden kann.
Wie so:
quelle
super.onActivityResult()
.Kurze und funktionierende Lösung:
Befolgen Sie einfache Schritte
Schritte
Schritt 1: Überschreiben Sie den
onSaveInstanceState
Status im jeweiligen Fragment. Und entfernen Sie die Super-Methode daraus.Schritt 2: Verwenden
fragmentTransaction.commitAllowingStateLoss( );
statt
fragmentTransaction.commit( );
während Fragmentoperationen.quelle
ACHTUNG , die Verwendung
transaction.commitAllowingStateLoss()
kann zu einer schlechten Erfahrung für den Benutzer führen. Weitere Informationen dazu, warum diese Ausnahme ausgelöst wird, finden Sie in diesem Beitrag .quelle
Ich habe eine schmutzige Lösung für diese Art von Problem gefunden. Wenn Sie Ihre
ActivityGroups
aus irgendeinem Grund behalten möchten (ich hatte zeitliche Einschränkungen), implementieren Sie sie einfachin Ihrem
Activity
und machen Sie einenback
Code dort. Selbst wenn es auf älteren Geräten keine solche Methode gibt, wird diese Methode von neueren aufgerufen.quelle
Verwenden Sie commitAllowingStateLoss () nicht. Es sollte nur in Fällen verwendet werden, in denen es in Ordnung ist, dass sich der UI-Status für den Benutzer unerwartet ändert.
https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss ()
Wenn die Transaktion in ChildFragmentManager von parentFragment ausgeführt wird, verwenden Sie stattdessen parentFragment.isResume () außerhalb, um dies zu überprüfen.
quelle
Ich hatte ein ähnliches Problem, das Szenario war wie folgt:
Das onCreate Methode der Aktivität war wie :
Die Ausnahme wurde ausgelöst, weil beim Ändern der Konfiguration (Gerät gedreht) die Aktivität erstellt wird, das Hauptfragment aus dem Verlauf des Fragmentmanagers abgerufen wird und gleichzeitig das Fragment bereits eine hat ALTEN Verweis auf die zerstörte Aktivität enthält
Durch Ändern der Implementierung wurde das Problem gelöst:
Sie müssen Ihre Listener jedes Mal festlegen, wenn die Aktivität erstellt wird, um zu vermeiden, dass die Fragmente Verweise auf alte zerstörte Instanzen der Aktivität enthalten.
quelle
Wenn Sie von erben
FragmentActivity
, müssen Sie die Oberklasse aufrufen inonActivityResult()
:Wenn Sie dies nicht tun und versuchen, in dieser Methode ein Fragmentdialogfeld anzuzeigen, erhalten Sie möglicherweise OPs
IllegalStateException
. (Um ehrlich zu sein, verstehe ich nicht ganz, warum der Superaufruf das Problem behebt. Er wurdeonActivityResult()
zuvor aufgerufenonResume()
, daher sollte es immer noch nicht erlaubt sein, ein Fragmentdialogfeld anzuzeigen.)quelle
Ich habe diese Ausnahme erhalten, als ich die Zurück-Taste gedrückt habe, um die Absichtsauswahl für meine Kartenfragmentaktivität abzubrechen. Ich habe dieses Problem behoben, indem ich den Code von onResume (wo ich das Fragment initialisiert habe) durch onstart () ersetzt habe und die App funktioniert einwandfrei. Ich hoffe, es hilft.
quelle
Ich denke, die Verwendung
transaction.commitAllowingStateLoss();
ist nicht die beste Lösung. Diese Ausnahme wird ausgelöst, wenn die Konfiguration der Aktivität geändert und das FragmentonSavedInstanceState()
aufgerufen wird und anschließend Ihre asynchrone Rückrufmethode versucht, ein Fragment festzuschreiben.Eine einfache Lösung könnte darin bestehen, zu überprüfen, ob die Aktivität die Konfiguration ändert oder nicht
zB prüfen
isChangingConfigurations()
dh
if(!isChangingConfigurations()) { //commit transaction. }
Kasse auch diesen Link
quelle
Möglicherweise bestand die reibungsloseste und einfachste Lösung, die ich in meinem Fall gefunden habe, darin, zu vermeiden, dass das fehlerhafte Fragment als Reaktion auf das Aktivitätsergebnis vom Stapel genommen wird. Also ändere diesen Anruf in meinem
onActivityResult()
:dazu:
hat in meinem Fall geholfen.
quelle
Wenn Sie in onActivityResult eine FragmentTransaction ausführen, können Sie in onActivityResult einen booleschen Wert festlegen. In onResume können Sie Ihre FragmentTransaction auf der Grundlage des booleschen Werts ausführen. Bitte beziehen Sie sich auf den Code unten.
quelle
Mit freundlicher Genehmigung: Lösung für IllegalStateException
Dieses Problem hatte mich lange geärgert, aber zum Glück hatte ich eine konkrete Lösung dafür. Eine ausführliche Erklärung finden Sie hier .
Die Verwendung von commitAllowStateloss () könnte diese Ausnahme verhindern, würde jedoch zu Unregelmäßigkeiten der Benutzeroberfläche führen. Bisher haben wir verstanden, dass eine IllegalStateException auftritt, wenn wir versuchen, ein Fragment festzuschreiben, nachdem der Aktivitätsstatus verloren gegangen ist. Daher sollten wir die Transaktion nur verzögern, bis der Status wiederhergestellt ist Es kann einfach so gemacht werden
Deklarieren Sie zwei private boolesche Variablen
Jetzt setzen und deaktivieren wir in onPostResume () und onPause unsere boolesche Variable isTransactionSafe. Die Idee ist, die Transaktion nur dann als sicher zu markieren, wenn die Aktivität im Vordergrund steht, damit keine Gefahr eines Statusverlusts besteht.
-Was wir bisher getan haben, wird vor IllegalStateException gespeichert, aber unsere Transaktionen gehen verloren, wenn sie ausgeführt werden, nachdem die Aktivität in den Hintergrund verschoben wurde, ähnlich wie commitAllowStateloss (). Um dies zu unterstützen, haben wir die boolesche Variable isTransactionPending
quelle
Fragmenttransaktionen sollten nicht danach ausgeführt werden
Activity.onStop()
! Stellen Sie sicher, dass Sie keine Rückrufe haben, nach denen eine Transaktion ausgeführt werden könnteonStop()
. Es ist besser, den Grund zu beheben, als zu versuchen, das Problem mit Ansätzen wie zu umgehen.commitAllowingStateLoss()
quelle
Ab der Support-Bibliothek Version 24.0.0 können Sie eine
FragmentTransaction.commitNow()
Methode aufrufen , die diese Transaktion synchron festschreibt, anstatt sie aufzurufen,commit()
gefolgt vonexecutePendingTransactions()
. Wie die Dokumentation sagt, ist dieser Ansatz noch besser:quelle
Wenn Sie versuchen, ein Fragment in Ihre Aktivität zu laden, stellen Sie sicher, dass die Aktivität wieder aufgenommen wird und nicht pausiert. Im Pausenstatus verlieren Sie möglicherweise den abgeschlossenen Festschreibungsvorgang.
Sie können transaction.commitAllowingStateLoss () anstelle von transaction.commit () verwenden, um das Fragment zu laden
oder
Erstellen Sie einen Booleschen Wert und prüfen Sie, ob die Aktivität nicht unterbrochen wird
dann beim Laden der Fragmentprüfung
quelle
Um dieses Problem zu umgehen, können wir die Navigationsarchitekturkomponente verwenden , die in Google I / O 2018 eingeführt wurde. Die Navigationsarchitekturkomponente vereinfacht die Implementierung der Navigation in einer Android-App.
quelle
In Bezug auf @Anthonyeef gute Antwort, hier ist ein Beispielcode in Java:
quelle
Wenn Sie mit der Methode popBackStack () oder popBackStackImmediate () abgestürzt sind, versuchen Sie es bitte mit:
Das funktioniert auch bei mir.
quelle
In meinem Fall habe ich diesen Fehler in einer Überschreibungsmethode namens onActivityResult erhalten. Nach dem Graben habe ich nur herausgefunden, dass ich vielleicht vorher ' super ' nennen musste.
Ich habe es hinzugefügt und es hat einfach funktioniert
Möglicherweise müssen Sie bei jeder Überschreibung, die Sie vor Ihrem Code ausführen, nur ein "Super" hinzufügen.
quelle
Kotlin Erweiterung
Verwendungszweck:
quelle
fragment: Fragment
? Ja, ich habe diese Variante ausprobiert, aber in diesem Fall wird in allen Fällen ein Fragment erstellt (auch wenn fragmentManager == null ist, aber ich bin auf diese Situation nicht gestoßen). Ich habe die Antwort aktualisiert und null geändert, um sie zu markierenaddToBackStack()
.Dieser Absturz ist darauf zurückzuführen, dass eine FragmentTransaction festgeschrieben wird, nachdem der Lebenszyklus der eigenen Aktivität bereits in SaveInstanceState ausgeführt wurde. Dies wird häufig durch das Festschreiben von FragmentTransactions aus einem asynchronen Rückruf verursacht. Weitere Informationen finden Sie in der verknüpften Ressource.
Fragmenttransaktionen & Aktivitätsstatusverlust
http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
quelle
Fügen Sie dies Ihrer Aktivität hinzu
quelle
Ich habe auch dieses Problem festgestellt und das Problem tritt jedes Mal auf, wenn sich der Kontext von Ihnen
FragmentActivity
ändert (z. B. wenn die Bildschirmausrichtung geändert wird usw.). Die beste Lösung dafür ist es, den Kontext von Ihrem zu aktualisierenFragmentActivity
.quelle
Am Ende habe ich ein Basisfragment erstellt und alle Fragmente in meiner App erweitern lassen
Wenn ich dann versuche, ein Fragment zu zeigen, verwende ich
showAllowingStateLoss
anstelle vonshow
so was:
Ich bin aus dieser PR auf diese Lösung gekommen: https://github.com/googlesamples/easypermissions/pull/170/files
quelle
Eine weitere mögliche Problemumgehung, bei der ich nicht sicher bin, ob sie in allen Fällen hilfreich ist (Ursprung hier ):
quelle
Ich weiß, dass es eine akzeptierte Antwort von @Ovidiu Latcu gibt, aber nach einiger Zeit bleibt der Fehler bestehen.
Crashlytics senden mir immer noch diese seltsame Fehlermeldung.
Allerdings tritt der Fehler jetzt nur bei Version 7+ (Nougat) auf. Mein Fix war die Verwendung von commitAllowingStateLoss () anstelle von commit () bei fragmentTransaction zu verwenden.
Dieser Beitrag ist hilfreich für commitAllowingStateLoss () und hatte nie wieder ein Fragmentproblem.
Zusammenfassend kann gesagt werden, dass die hier akzeptierte Antwort auf Android-Versionen vor Nougat funktioniert.
Dies kann jemandem einige Stunden der Suche ersparen. fröhliche Codierungen. <3 Prost
quelle