Ausnahme "IllegalStateException: Diese Aktion kann nach onSaveInstanceState nicht ausgeführt werden"

355

Ich habe eine Live-Android-Anwendung, und vom Markt habe ich folgende Stapelverfolgung erhalten, und ich habe keine Ahnung, warum dies geschieht, da es nicht im Anwendungscode geschieht, sondern durch das eine oder andere Ereignis aus der Anwendung verursacht wird (Annahme)

Ich verwende keine Fragmente, dennoch gibt es eine Referenz von FragmentManager. Wenn jemand etwas Licht auf versteckte Fakten werfen kann, um diese Art von Problem zu vermeiden:

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.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
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.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:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
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:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  
dcool
quelle
Haben Sie schon eine Lösung gefunden? Ich habe
nhaarman
1
Ich hatte das gleiche Problem und fand eine einfache Lösung, die für mich funktioniert
Phlebas
2
@phlebas Nein, hast du nicht. Ihre betrifft Dialoge, und dies ist nicht der Fall. Die oberste Zeile Ihres Stack-Trace-Abgleichs reicht nicht aus. Der Rest ist ganz anders. Ich sage das, weil ich mir gerade Ihr Problem angesehen habe und es mir leider nicht hilft.
Themightyjon
Verwenden Sie in dieser Aktivität einen Thread oder eine AsynTask?
José Castro
21
Ich diskutiere diesen Fehler in meinem Blog-Beitrag ... Sie sollten ihn lesen. :)
Alex Lockwood

Antworten:

455

Dies ist der dümmste Fehler, dem ich bisher begegnet bin. Ich hatte eine FragmentAnwendung, die perfekt für API <11 und Force Closingfür API> 11 funktionierte .

Ich konnte wirklich nicht herausfinden, was sie innerhalb des ActivityLebenszyklus des Aufrufs an geändert haben saveInstance, aber hier ist, wie ich das gelöst habe:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

Ich rufe einfach nicht an .super()und alles funktioniert super. Ich hoffe, das spart Ihnen etwas Zeit.

BEARBEITEN: Nach einigen weiteren Recherchen ist dies ein bekannter Fehler im Support-Paket.

Wenn Sie die Instanz speichern und etwas zu Ihrer hinzufügen müssen, können outState BundleSie Folgendes verwenden:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2: Dies kann auch auftreten, wenn Sie versuchen, eine Transaktion auszuführen, nachdem Sie Activityim Hintergrund verschwunden sind. Um dies zu vermeiden, sollten Sie verwendencommitAllowingStateLoss()

EDIT3: Die oben genannten Lösungen haben Probleme in den frühen support.v4-Bibliotheken behoben, soweit ich mich erinnern kann. Aber wenn Sie immer noch Probleme mit diesem haben Sie MÜSSEN auch lesen @AlexLockwood : ‚s Blog Fragment Transaktionen & Aktivitätszustand Verlust

Zusammenfassung aus dem Blog-Beitrag (aber ich empfehle Ihnen dringend, ihn zu lesen):

  • NIEMALS commit() Transaktionen nach onPause()Pre-Honeycomb und onStop()Post-Honeycomb
  • Seien Sie vorsichtig, wenn Sie Transaktionen innerhalb von ActivityLebenszyklusmethoden ausführen. Verwendung onCreate() , onResumeFragments()undonPostResume()
  • Vermeiden Sie Transaktionen innerhalb asynchroner Rückrufmethoden
  • Verwenden Sie commitAllowingStateLoss()nur als letztes Mittel
Ovidiu Latcu
quelle
97
Sie sollten commitAllowingStateLoss () anstelle von commit () verwenden
meh
7
Wenn Sie also onSaveInstanceState nicht als Super aufrufen, kann der FragmentManager den Status aller Fragmente nicht mehr speichern und wiederherstellen. Dies kann zu Rotationsproblemen führen. Außerdem habe ich gerade versucht, Junk in das Bundle zu packen, und das macht für mich keinen Unterschied. Ich bin mir nicht sicher, wie es wäre - der Fehler, auf den Sie im Support-Paket verwiesen haben, ist eine NullPointerException und scheint dieser IllegalStateException nicht sehr ähnlich zu sein ...
themightyjon
56
@meh commitAllowingStateLoss()vermeidet nur die Ausnahme. Es schützt Ihre Anwendung nicht vor versehentlichem Statusverlust. Siehe diesen Blog-Beitrag .
Alex Lockwood
2
@AlexLockwood Aus diesem Blog-Beitrag können wir also lernen, dass wir alle unsere Netzwerkaufrufe innerhalb des Fragments ausführen sollten (und bei Bedarf einige temporäre Fortschrittsanzeigen anzeigen sollten). Nur so können wir vermeiden, dass diese Ausnahme auftritt, wenn sie wahrscheinlich aufgrund eines Commits auftritt wird nach einem asynchronen Methodenaufruf aufgerufen.
meh
1
Ich verstehe nicht den ersten Punkt: "NIEMALS () Transaktionen nach ... onStop () auf Post-Honeycomb festschreiben". Was ist, wenn ich eine Schaltfläche benötige, um ein Fragment auszulösen, das durch ein anderes ersetzt werden soll? Sollte ich einen Booleschen Wert einfügen, der prüft, ob die Aktivität bei Stop beendet wurde, und wenn ja, stattdessen commitAllowingStateLoss aufrufen? Was ist auch, wenn ich ein Fragment in einem Fragment habe, das ich beim Klicken auf eine Schaltfläche ersetzen muss?
Android-Entwickler
76

Wenn Sie im Android-Quellcode nach den Ursachen dieses Problems suchen, hat das Flag mStateSaved in FragmentManagerImplclass (Instanz in Activity verfügbar) den Wert true. Es wird auf true gesetzt, wenn der Backstack beim Aufruf von gespeichert wird (saveAllState) Activity#onSaveInstanceState. Danach setzen die Aufrufe von ActivityThread dieses Flag nicht mit den verfügbaren Rücksetzmethoden von FragmentManagerImpl#noteStateNotSaved()und zurück dispatch().

So wie ich es sehe, gibt es einige verfügbare Korrekturen, je nachdem, was Ihre App tut und verwendet:

Gute Wege

Vor allem: Ich würde für Alex Lockwood Artikel werben . Dann von dem, was ich bisher gemacht habe:

  1. Rufen Sie für Fragmente und Aktivitäten, für die keine Statusinformationen erforderlich sind , commitAllowStateLoss auf . Entnommen aus der Dokumentation:

    Ermöglicht die Ausführung des Commits, nachdem der Status einer Aktivität gespeichert wurde. Dies ist gefährlich, da das Festschreiben verloren gehen kann, wenn die Aktivität später aus ihrem Status wiederhergestellt werden muss. Dies sollte daher nur in Fällen verwendet werden, in denen es in Ordnung ist, dass sich der UI-Status für den Benutzer unerwartet ändert. " Ich denke, dies ist in Ordnung zu verwenden, wenn das Fragment schreibgeschützte Informationen anzeigt. Oder verwenden Sie die Rückrufmethoden, um die bearbeiteten Informationen beizubehalten, selbst wenn sie bearbeitbare Informationen anzeigen.

  2. commit()Rufen Sie kurz nach dem Festschreiben der Transaktion (Sie haben gerade angerufen ) an FragmentManager.executePendingTransactions().

Nicht empfohlene Methoden:

  1. Wie Ovidiu Latcu oben erwähnt hat, rufen Sie nicht an super.onSaveInstanceState(). Dies bedeutet jedoch, dass Sie den gesamten Status Ihrer Aktivität zusammen mit dem Fragmentstatus verlieren.

  2. Überschreiben onBackPressedund nur dort anrufen finish(). Dies sollte in Ordnung sein, wenn Ihre Anwendung die Fragment-API nicht verwendet. wie in super.onBackPressedgibt es einen anruf an FragmentManager#popBackStackImmediate().

  3. Wenn Sie beide Fragment-APIs verwenden und der Status Ihrer Aktivität wichtig / wichtig ist, können Sie versuchen, mithilfe der Reflection-API aufzurufen FragmentManagerImpl#noteStateNotSaved(). Aber dies ist ein Hack, oder man könnte sagen, es ist eine Problemumgehung. Ich mag es nicht, aber in meinem Fall ist es ziemlich akzeptabel, da ich einen Code aus einer Legacy-App habe, der ( TabActivityund implizit LocalActivityManager) veralteten Code verwendet .

Unten ist der Code, der Reflektion verwendet:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

Prost!

Gunar
quelle
Dies scheint auch mit ActionBarSherlock unter Gingerbread zu passieren, so dass der Fall der Überprüfung der Build-
ID strittig
Außerdem sollten Sie darauf hinweisen - das ist auch nicht für den ABS-Einsatz geeignet :)
t0mm13b
@ t0mm13b: Der obige Code steht für mein Projekt, da weder Fragmente noch Unterstützung verwendet werden. FragmentActivity. Es läuft auf android.app.Activity und da die Inkonsistenz, die die Ausnahme auslöst, durch FragmentManager (API-Level 11 aufwärts) verursacht wird, ist die Prüfung der Grund ... Wenn Sie glauben, dass die Quelle des Bösen auch für Sie dieselbe ist, fühlen Sie sich frei um den Scheck zu entfernen. ABS ist eine andere Geschichte, da es über dem Kompatibilitätspaket und der Implementierung von Support ausgeführt wird. FragmentActivity verwendet möglicherweise dieselbe Implementierung von FragmentManager und voila: dasselbe Problem.
Gunar
@ t0mm13b: Um mehr hinzuzufügen, da 600 Zeichen nicht ausreichen, müssen Sie zuerst untersuchen, was dies wirklich für Sie verursacht. Sie müssen auch erkennen, dass oben ein hässlicher Hack ist und ich keine Verantwortung übernehme, ob er läuft oder nicht (für mich war die beste Lösung unter den gegebenen Umständen). Wenn Sie es verwenden müssen, überprüfen Sie den kompatiblen Quellcode auf Variablennamen, da diese möglicherweise vom Standardpaket abweichen. Ich würde hoffen, dass dieses Problem durch die nächsten Versionen des Kompatibilitätspakets behoben wird, aber aus Android-Erfahrung gibt es nur wenige Chancen ...
Gunar
Uhhh ... Es ist genau das gleiche Problem wie in diesem Fehlerbericht, um den es in dieser OP-Frage geht. Ich von meinem Kommentar stehen - Sie sollten explizit in Haftungsausschluss gesetzt haben und sagen , es ist nicht garantiert und auch sollte festgestellt haben , dass Sie nicht Fragmente verwendet haben - sonst stören , warum veröffentlichen die Antwort entweder! :) Nur sagen ...
t0mm13b
35

Eine solche Ausnahme tritt auf, wenn Sie versuchen, einen Fragmentübergang durchzuführen, nachdem Ihre Fragmentaktivität onSaveInstanceState()aufgerufen wurde.

Ein Grund dafür kann sein, dass Sie ein AsyncTask(oder Thread) laufen lassen, wenn eine Aktivität gestoppt wird.

Übergänge nach dem onSaveInstanceState()Aufruf können möglicherweise verloren gehen, wenn das System die Aktivität für Ressourcen zurückfordert und sie später neu erstellt.

FunkTheMonk
quelle
2
Hey Funk, ich habe hier eine Frage, warum onBackPressed für diese Aktivität oder dieses Fragment aufgerufen werden kann, wenn diese Aktivität oder dieses Fragment gestoppt wurde. Die obige Ausnahme scheint durch ein UI-Ereignis (dh Drücken der Taste BACK) generiert worden zu sein. Ich kann jedoch die Beziehung zwischen der Async-Task und der Zurück-Taste nicht herausfinden.
dcool
Da Sie Fragmentübergänge in den Back-Zustand speichern können, kann das Drücken der Back-Taste die Umkehrung des von Ihnen gespeicherten Übergangs bewirken (so dass alte Fragmente zurückkehren). onSaveInstanceState wird aufgerufen, bevor Ihre Aktivität zerstört wird, um Ressourcen im System wiederherzustellen, nicht immer, nachdem onStop aufgerufen wurde. Entschuldigung, das war in meiner Antwort nicht sehr klar.
FunkTheMonk
Funk, aber ich verwende keine Fragmente in meiner Anwendung. Dies können die im nativen Code verwendeten Fragmente sein. früher dachte ich, du sprichst über dasselbe.
dcool
1
Ich hatte eine AsyncTask mit einem Verweis auf ein Fragment. Problem behoben, nachdem der super () -Aufruf aus onSaveInstanceState entfernt und die Referenz aus meiner AsyncTask durch eine WeakReference <Fragment> ersetzt wurde.
Buffalo
2
@ Buffalo Das ist definitiv keine Lösung für das Problem. Sie sollten immer anrufen super.onSaveInstanceState().
Alex Lockwood
27

Rufen Sie einfach super.onPostResume () auf, bevor Sie Ihr Fragment anzeigen, oder verschieben Sie Ihren Code nach dem Aufruf von super.onPostResume () in die Methode onPostResume (). Dies löst das Problem!

MJ.Ahmadi
quelle
6
Das Aufrufen von onPostResume () stellt sicher, dass onResumeFragments () aufgerufen wird, und für mich ist dies die ideale Lösung.
j2emanue
20

Dies kann auch passieren, wenn dismiss()ein Dialogfragment aufgerufen wird, nachdem der Bildschirm gesperrt und ausgeblendet und der Instanzstatus des Dialogfelds Aktivität + gespeichert wurde. Um diesen Anruf zu umgehen:

dismissAllowingStateLoss()

Buchstäblich jedes Mal, wenn ich einen Dialog entlasse, ist mir der Status sowieso egal, also ist das in Ordnung - Sie verlieren eigentlich keinen Status.

Brian Dilley
quelle
2
Das war genau mein Problem! Sie, mein Herr, sind brillant!
Tash Pemhiwa
17

Kurze und funktionierende Lösung:

Befolgen Sie einfache Schritte:

Schritt 1 : Überschreiben Sie den onSaveInstanceState-Status im jeweiligen Fragment. Und entfernen Sie die Super-Methode daraus.

@Override
public void onSaveInstanceState(Bundle outState) {
};

Schritt 2 : Verwenden Sie CommitAllowingStateLoss (); anstelle von commit (); während Fragmentoperationen.

fragmentTransaction.commitAllowingStateLoss();
Basbous
quelle
1
Das Entfernen der Supermethode hat den Trick gemacht. Können Sie erklären, warum? Ist es sicher, das zu entfernen?
Bruce
7
Es ist nicht sicher, super () zu entfernen. Danach verlieren Sie andere Datenkonf!
Deadfish
12

Ich denke, der Lifecycle-Status kann helfen, einen solchen Absturz ab der Android-Unterstützung lib v26.1.0 zu verhindern. Sie können die folgende Überprüfung durchführen lassen:

if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)){
  // Do fragment's transaction commit
}

oder Sie können versuchen:

Fragment.isStateSaved()

Weitere Informationen finden Sie hier https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved ()

Jamal
quelle
7

das hat bei mir funktioniert ... habe das selbst herausgefunden ... hoffe es hilft dir!

1) keinen globalen "statischen" FragmentManager / FragmentTransaction haben.

2) onCreate initialisiert den FragmentManager IMMER erneut!

Beispiel unten: -

public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSavedInstanceState = savedInstanceState;
    setDefaultFragments();
}

protected void setDefaultFragments() {
    fragmentManager = getSupportFragmentManager();
    //check if on orientation change.. do not re-add fragments!
    if(mSavedInstanceState == null) {
        //instantiate the fragment manager

        fragmentTransaction = fragmentManager.beginTransaction();

        //the navigation fragments
        NavigationFragment navFrag = new NavigationFragment();
        ToolbarFragment toolFrag = new ToolbarFragment();

        fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
        fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
        fragmentTransaction.commitAllowingStateLoss();

        //add own fragment to the nav (abstract method)
        setOwnFragment();
    }
}
kurayami88
quelle
6

Ich habe das immer bekommen, wenn ich versucht habe, ein Fragment in der onActivityForResult () -Methode anzuzeigen, also war das Problem das nächste:

  1. Meine Aktivität wird angehalten und gestoppt, was bedeutet, dass onSaveInstanceState () bereits aufgerufen wurde (sowohl für Geräte vor als auch nach Honeycomb).
  2. Im Falle eines Ergebnisses habe ich eine Transaktion durchgeführt, um ein Fragment anzuzeigen / auszublenden, was diese IllegalStateException verursacht.

Was ich gemacht habe ist als nächstes:

  1. Mehrwert für die Bestimmung, ob die gewünschte Aktion ausgeführt wurde (z. B. Fotografieren von camere - isPhotoTaken) - Dies kann ein boolescher oder ein ganzzahliger Wert sein, je nachdem, wie viele verschiedene Transaktionen Sie benötigen.
  2. Bei der Methode override onResumeFragments () habe ich nach meinem Wert gesucht und nach Durchführung von Fragmenttransaktionen, die ich benötigte. In diesem Fall wurde commit () nach onSaveInstanceState nicht ausgeführt, da der Status in der onResumeFragments () -Methode zurückgegeben wurde.
Array
quelle
5

Ich habe das Problem mit onconfigurationchanged gelöst. Der Trick ist, dass je nach Lebenszyklus der Android-Aktivität, wenn Sie explizit eine Absicht (Kameraabsicht oder eine andere) genannt haben; In diesem Fall wird die Aktivität angehalten und onsavedInstance aufgerufen. Wenn Sie das Gerät in eine andere Position als die drehen, in der die Aktivität aktiv war; Das Ausführen von Fragmentoperationen wie Fragment Commit führt zu einer Ausnahme für den unzulässigen Status. Es gibt viele Beschwerden darüber. Es geht um das Management des Lebenszyklus von Android-Aktivitäten und um richtige Methodenaufrufe. Um dies zu lösen, habe ich Folgendes getan: 1-Überschreiben Sie die onsavedInstance-Methode Ihrer Aktivität und bestimmen Sie die aktuelle Bildschirmausrichtung (Hoch- oder Querformat). Stellen Sie dann Ihre Bildschirmausrichtung darauf ein, bevor Ihre Aktivität angehalten wird. Auf diese Weise sperren Sie die Bildschirmdrehung für Ihre Aktivität, falls sie von einer anderen gedreht wurde. 2-dann überschreiben Sie die Wiederaufnahme der Aktivitätsmethode und stellen Sie Ihren Orientierungsmodus jetzt auf Sensor ein, sodass nach dem Aufrufen der gespeicherten Methode eine weitere Konfiguration aufgerufen wird, um die Rotation ordnungsgemäß zu verarbeiten.

Sie können diesen Code kopieren / in Ihre Aktivität einfügen, um damit umzugehen:

@Override
protected void onSaveInstanceState(Bundle outState) {       
    super.onSaveInstanceState(outState);

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 
Douggynix
quelle
4

Ich hatte das gleiche Problem, IllegalStateException zu erhalten, aber das Ersetzen aller meiner Aufrufe von commit () durch commitAllowingStateLoss () hat nicht geholfen.

Der Schuldige war ein Aufruf von DialogFragment.show ().

Ich umgebe es mit

try {
    dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
    return;
}

und das hat es geschafft. OK, ich kann den Dialog nicht anzeigen, aber in diesem Fall war das in Ordnung.

Es war der einzige Ort in meiner App, an dem ich FragmentManager.beginTransaction () zum ersten Mal aufgerufen habe, aber nie commit () aufgerufen habe. Daher habe ich es nicht gefunden, als ich nach "commit ()" gesucht habe.

Das Lustige ist, dass der Benutzer die App nie verlässt. Stattdessen war der Killer eine AdMob-Interstitial-Anzeige.

Roger CS Wernersson
quelle
3
Hier gilt das gleiche. Ich habe das Überschreiben der Methode 'show (FragmentManager Manager, String tag)' gelöst und 'commit' durch 'commitAllowingStateLoss' ersetzt. etwas verlieren, weil ich nicht zwei private Attribute des Dialogs festlegen kann: mDismissed und mShownByMe. Aber es scheint jedes Mal zu funktionieren :)
Francesco Ditrani
Ich habe eine alternative Lösung zu DialogFragment entwickelt, mit der diese Ausnahme vermieden werden kann: github.com/AndroidDeveloperLB/DialogShard
Android-Entwickler
4

Meine Lösung für dieses Problem war

Fügen Sie im Fragment Methoden hinzu:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ...
    guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
    guideMap = guideMapFragment.getMap();
    ...
}

@Override
public void onDestroyView() {
    SherlockFragmentActivity a = getSherlockActivity();
    if (a != null && guideMapFragment != null) {
        try {
            Log.i(LOGTAG, "Removing map fragment");
            a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
            guideMapFragment = null;
        } catch(IllegalStateException e) {
            Log.i(LOGTAG, "IllegalStateException on exit");
        }
    }
    super.onDestroyView();
}

Mag schlecht sein, konnte aber nichts Besseres finden.

mc.dev
quelle
Trues .. das Abfangen der Ausnahme kann den Absturz der Anwendung vermeiden, aber Verhaltensprobleme wie Fragmente, die auf dem Bildschirm verbleiben oder nicht hinzugefügt wurden.
Marcos Vasconcelos
4
Scrolle weiter. Die Wahrheit ist da draußen
bis zum
4

Ich habe dieses Problem. Aber ich denke, dieses Problem hängt nicht mit Commit und CommitAllowStateLoss zusammen.

Die folgende Stack-Trace- und Ausnahmemeldung befasst sich mit commit ().

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

Diese Ausnahme wurde jedoch durch onBackPressed () verursacht.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)

Sie wurden alle von checkStateLoss () verursacht

private void checkStateLoss() {
    if (mStateSaved) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
    if (mNoTransactionsBecause != null) {
        throw new IllegalStateException(
                "Can not perform this action inside of " + mNoTransactionsBecause);
    }

mStateSaved ist nach onSaveInstanceState wahr.

Dieses Problem tritt selten auf. Ich bin noch nie auf dieses Problem gestoßen. Ich kann das Problem nicht erneut auftreten.

Ich habe das Problem 25517 gefunden

Dies kann unter folgenden Umständen geschehen sein

  1. Die Zurück-Taste wird nach onSaveInstanceState aufgerufen, jedoch bevor die neue Aktivität gestartet wird.

  2. Verwenden Sie onStop () im Code

Ich bin mir nicht sicher, wo die Wurzel des Problems liegt. Also habe ich einen hässlichen Weg benutzt.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}
oO_ox
quelle
Ich habe das Problem nicht wirklich gelöst, aber dieses Problem hängt nicht mit commit und commitAllowStateLoss zusammen.
oO_ox
4

Ich habe das gleiche Problem in meiner App. Ich habe dieses Problem gelöst, indem ich nur die super.onBackPressed();vorherige Klasse und commitAllowingStateLoss()die aktuelle Klasse mit diesem Fragment aufgerufen habe.

Peter
quelle
2
Vielen Dank. Diese Lösung löste das Problem uisng commitAllowingStateLoss()anstelle voncommit()
Chintak Patel
Vermeiden Sie die Verwendung von commitAllowingStateLoss () medium.com/@elye.project/…
swooby
3

onSaveInstance wird aufgerufen, wenn ein Benutzer den Bildschirm dreht, damit er Ressourcen laden kann, die der neuen Ausrichtung zugeordnet sind.

Es ist möglich, dass dieser Benutzer den Bildschirm gedreht und anschließend die Zurück-Taste gedrückt hat (da dieser Benutzer möglicherweise auch sein Telefon während der Verwendung Ihrer App gefummelt hat).

dols
quelle
2
Konfigurationsänderungen (z. B. Orientierungsänderungen) können zwar zu dieser Ausnahme führen, sind jedoch nicht die Hauptursache.
Alex Lockwood
2

Gleiches Problem von mir und nach einem Tag Analyse aller Artikel, Blogs und Stackoverflow habe ich eine einfache Lösung gefunden. Verwenden Sie savedInstanceState überhaupt nicht. Dies ist die Bedingung mit einer Codezeile. Auf dem Fragmentcode:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....
Tobia Caneschi
quelle
2

Dies geschieht immer dann, wenn Sie versuchen, ein Fragment zu laden, die Aktivität jedoch ihren Status in onPause () geändert hat. Dies geschieht beispielsweise, wenn Sie versuchen, Daten abzurufen und in die Aktivität zu laden, aber bis der Benutzer auf eine Schaltfläche geklickt hat und dies getan hat zur nächsten Aktivität übergegangen.

Sie können dies auf zwei Arten lösen

Sie können transaction.commitAllowingStateLoss () anstelle von transaction.commit () verwenden, um das Fragment zu laden. Möglicherweise verlieren Sie jedoch den durchgeführten Commit-Vorgang.

oder

Stellen Sie sicher, dass die Aktivität fortgesetzt wird und der Status beim Laden eines Fragments nicht angehalten wird. Erstellen Sie einen Booleschen Wert und prüfen Sie, ob die Aktivität nicht in den Status onPause () wechselt.

@Override
public void onResume() {
    super.onResume();
    mIsResumed = true;
}

@Override
public void onPause() {
    mIsResumed = false;
    super.onPause();
}

Überprüfen Sie dann beim Laden des Fragments, ob Aktivität vorhanden ist, und laden Sie es nur, wenn die Aktivität im Vordergrund steht.

if(mIsResumed){
 //load the fragment
}
Sharath Kumar
quelle
1

Danke @gunar, aber ich denke, es gibt einen besseren Weg.

Laut doc:

 * If you are committing a single transaction that does not modify the
 * fragment back stack, strongly consider using
 * {@link FragmentTransaction#commitNow()} instead. This can help avoid
 * unwanted side effects when other code in your app has pending committed
 * transactions that expect different timing.
 *
 * @return Returns true if there were any pending transactions to be
 * executed.
 */
public abstract boolean executePendingTransactions();

Verwenden Sie also commitNow, um Folgendes zu ersetzen:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
JianxinLi
quelle
0

Nun, nachdem ich alle oben genannten Lösungen ohne Erfolg ausprobiert habe (weil ich im Grunde keine Transaktionen habe).

In meinem Fall habe ich AlertDialogs und ProgressDialog als Fragmente verwendet, bei denen manchmal bei Rotation, wenn ich nach dem FragmentManager frage, der Fehler auftritt.

Ich habe eine Problemumgehung gefunden, bei der viele ähnliche Beiträge gemischt wurden:

Es handelt sich um eine dreistufige Lösung, die alle auf Ihrer FragmentActivity ausgeführt wird (in diesem Fall GenericActivity):

private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    //To avoid bug for fragments: Step 2 of 3
    activity = new WeakReference<GenericActivity>(this);
}

@Override
public FragmentManager getSupportFragmentManager(){
    //To avoid bug for fragments: Step 3 of 3
    if (this == activity.get()) {
        return super.getSupportFragmentManager();
    }
    return activity.get().getSupportFragmentManager();
}
Antilope
quelle
0

Wenn ich Startaktivität in einem Fragment verwende, erhalte ich diese Ausnahme;

Wenn ich startactivityforresult ändere, ist die Ausnahme weg :)

Der einfachste Weg, dies zu beheben, ist die Verwendung der API startActivityForResult :)

Frage
quelle
0

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 und die Transaktion festgeschrieben habe) durch onStart () ersetzt habe. Die App funktioniert jetzt einwandfrei. Ich hoffe es hilft.

DCS
quelle
0

Dies ist in Android 4.2 und auch in der Quelle der Support-Bibliothek behoben. [*]

Einzelheiten zur Ursache (und zu den Problemumgehungen) finden Sie im Google-Fehlerbericht: http://code.google.com/p/android/issues/detail?id=19917

Wenn Sie die Support-Bibliothek verwenden, sollten Sie sich (lange) keine Sorgen um diesen Fehler machen müssen [*]. Wenn Sie die API jedoch direkt verwenden (dh den FragmentManager der Support-Bibliothek nicht verwenden) und auf eine API unter Android 4.2 abzielen, müssen Sie eine der Problemumgehungen ausprobieren.

[*] Zum Zeitpunkt des Schreibens verteilt der Android SDK Manager noch eine alte Version, die diesen Fehler aufweist.

Bearbeiten Ich werde hier einige Klarstellungen hinzufügen, da ich offensichtlich irgendwie verwirrt habe, wer diese Antwort abgelehnt hat.

Es gibt verschiedene (aber verwandte) Umstände, die dazu führen können, dass diese Ausnahme ausgelöst wird . Meine obige Antwort bezieht sich auf die spezifische Instanz, die in der Frage besprochen wurde, dh einen Fehler in Android, der später behoben wurde. Wenn Sie diese Ausnahme aus einem anderen Grund erhalten, liegt dies daran, dass Sie Fragmente hinzufügen / entfernen, wenn dies nicht der Fall sein sollte (nachdem Fragmentzustände gespeichert wurden). Wenn Sie sich in einer solchen Situation befinden, kann Ihnen möglicherweise " Verschachtelte Fragmente - IllegalStateException" Diese Aktion kann nach onSaveInstanceState nicht ausgeführt werden " von Nutzen sein.

Benjamin Dobell
quelle
0

Mein Anwendungsfall: Ich habe Listener in Fragment verwendet, um Aktivitäten darüber zu informieren, dass etwas passiert ist. Ich habe ein neues Fragment-Commit für die Rückrufmethode durchgeführt. Dies funktioniert beim ersten Mal einwandfrei. Bei einer Änderung der Ausrichtung wird die Aktivität jedoch mit dem gespeicherten Instanzstatus neu erstellt. In diesem Fall wird das Fragment nicht erneut erstellt, was bedeutet, dass das Fragment den Listener hat, bei dem es sich um eine alte zerstörte Aktivität handelt. Auf jede Art und Weise wird die Rückrufmethode bei einer Aktion ausgelöst. Es geht um zerstörte Aktivitäten, die das Problem verursachen. Die Lösung besteht darin, den Listener fragmentarisch mit der aktuellen Live-Aktivität zurückzusetzen. Dies löst das Problem.

Ganesh Kanna
quelle
0

Ich habe festgestellt, dass fast jede Hintergrund-App mit diesem Fehler abstürzt, wenn eine andere App vom Typ Dialog ist und das Senden von Berührungen an die Hintergrund-App ermöglicht. Ich denke, wir müssen jedes Mal überprüfen, wenn eine Transaktion ausgeführt wird, ob die Instanz gespeichert oder wiederhergestellt wurde.

chris
quelle
0

In meinem Fall habe ich mit der gleichen Fehlerausnahme "onBackPressed ()" in eine ausführbare Datei eingefügt (Sie können jede Ihrer Ansichten verwenden):

myView.post(new Runnable() {
                    @Override
                    public void run() {
                        onBackPressed()
                    }
                });

Ich verstehe nicht warum, aber es funktioniert!

Alecs
quelle
Beim Posten in einer Ansicht wird Runnable erst ausgeführt, nachdem die Ansicht ordnungsgemäß angelegt und auf den Bildschirm gezeichnet wurde. Dies bedeutet oft, dass die Aktivität selbst vollständig wieder aufgenommen wurde, daher keine Probleme
Mercato
0

Möglicherweise rufen Sie fragmentManager.popBackStackImmediate () auf. wenn die Aktivität unterbrochen ist. Die Aktivität ist noch nicht beendet, wird jedoch angehalten und steht nicht im Vordergrund. Sie müssen vor popBackStackImmediate () überprüfen, ob die Aktivität angehalten wurde oder nicht.

Murat
quelle
0

Mir ist etwas sehr Interessantes aufgefallen. Ich habe in meiner App die Möglichkeit, die Galerie des Telefons zu öffnen, und das Gerät fragt, welche App verwendet werden soll. Dort klicke ich auf den grauen Bereich außerhalb des Dialogfelds und sehe dieses Problem. Ich habe festgestellt, wie meine Aktivität von onPause, onSaveInstanceState zurück zu onResume geht. OnCreateView wird nicht besucht. Ich mache Transaktionen bei onResume. Am Ende habe ich ein Flag gesetzt, das in Pause negiert wird, in CreateView jedoch wahr ist. Wenn das Flag onResume true ist, führen Sie onCommit aus, andernfalls commitAllowingStateLoss. Ich könnte so viel Zeit verschwenden, aber ich wollte den Lebenszyklus überprüfen. Ich habe ein Gerät, das sdkversion 23 ist, und ich bekomme dieses Problem nicht, aber ich habe ein anderes, das 21 ist, und dort sehe ich es.

Juan Mendez
quelle
-1

Sie können FragmentActivity.onStart vor popBackStackImmediate verwenden

so was:

public void backStackFragment() {
    this.start();
    getFragmentManager().popBackStackImmediate();
}

public void start(){
    FragmentActivity a = getActivity();
    if(a instanceof DepositPlanPadActivity){
      ((DepositPlanPadActivity)a).onStart();
    }
    if(a instanceof SmallChangePlanPad){
            ((SmallChangePlanPad)a).onStart();
        }
        if(a instanceof UserCenterActivity){
            ((UserCenterActivity)a).onStart();
        }
    }

http://jorryliu.blogspot.com/2014/09/illegalstateexception-can-not-perform.html

user3327339
quelle