370 Stimmen

erhält die Ausnahme "IllegalStateException: Kann diese Aktion nach onSaveInstanceState nicht ausführen"

Ich habe eine Live-Android-Anwendung, und aus dem Markt habe ich folgende Stack-Trace erhalten und ich habe keine Ahnung, warum seine geschieht, wie seine nicht in Anwendungscode geschieht, aber seine immer verursacht durch einige oder das andere Ereignis aus der Anwendung (Annahme)

Ich verwende keine Fragmente, dennoch gibt es einen Verweis auf FragmentManager. Wenn jeder Körper kann etwas Licht auf einige versteckte Fakten werfen, 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)

469voto

Ovidiu Latcu Punkte 70281

Dies ist der dümmste Fehler, der mir bisher untergekommen ist. Ich hatte eine Fragment Anwendung funktioniert perfekt für API < 11 y Force Closing sur API > 11 .

Ich konnte wirklich nicht herausfinden, was sie im Inneren der Activity Lebenszyklus beim Aufruf von saveInstance aber ich habe das Problem folgendermaßen gelöst:

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

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

EDITAR: Nach weiteren Recherchen ist dies eine bekannte Fehler im Support-Paket.

Wenn Sie die Instanz speichern müssen und etwas zu Ihrer outState Bundle können Sie 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 durchzuführen, nachdem Sie Activity ist im Hintergrund verschwunden. Um dies zu vermeiden, sollten Sie commitAllowingStateLoss()

EDIT3: Die oben genannten Lösungen behoben Probleme in den frühen support.v4-Bibliotheken, soweit ich mich erinnern kann. Aber wenn Sie noch Probleme mit diesem Sie haben MUSS Lesen Sie auch @AlexLockwood ''s blog : Fragmentierte Transaktionen und Verlust des Aktivitätsstatus

Zusammenfassung aus dem Blogbeitrag (aber ich empfehle Ihnen dringend, ihn zu lesen) :

  • NIEMALS commit() Transaktionen nach onPause() auf der Vor-Honigwabe, und onStop() auf der Post-Honigwabe
  • Seien Sie vorsichtig bei der Durchführung von Transaktionen innerhalb Activity Lebenszyklusmethoden. Utilice onCreate() , onResumeFragments() y onPostResume()
  • Vermeiden Sie die Durchführung von Transaktionen innerhalb asynchroner Rückrufmethoden
  • Utilice commitAllowingStateLoss() nur als letztes Mittel

76voto

gunar Punkte 14600

Wenn man im Android-Quellcode nachschaut, was dieses Problem verursacht, findet man das Flag mStateSaved in FragmentManagerImpl Klasse (Instanz verfügbar in Activity) hat den Wert true. Er wird auf true gesetzt, wenn der Backstack gespeichert wird (saveAllState) beim Aufruf von Activity#onSaveInstanceState . Danach setzen die Aufrufe von ActivityThread dieses Flag nicht mehr zurück, indem sie die verfügbaren Reset-Methoden von FragmentManagerImpl#noteStateNotSaved() y dispatch() .

So wie ich es sehe, gibt es einige mögliche Lösungen, je nachdem, was Ihre Anwendung tut und verwendet:

Gute Wege

Vor allem: Ich würde Werbung machen Alex Lockwood Artikel . Dann, nach dem, was ich bisher getan habe:

  1. Für Fragmente und Aktivitäten, bei denen keine staatlichen Informationen gespeichert werden müssen, rufen Sie commitAllowStateLoss . Aus der Dokumentation entnommen:

    Ermöglicht die Ausführung der Übergabe, nachdem der Zustand einer Aktivität gespeichert wurde. Dies ist gefährlich, weil die Übergabe verloren gehen kann, wenn die Aktivität später aus ihrem Zustand wiederhergestellt werden muss, daher sollte dies nur in Fällen verwendet werden, in denen es in Ordnung ist, dass sich der Zustand der Benutzeroberfläche für den Benutzer unerwartet ändert. Ich vermute, dass dies in Ordnung ist, wenn das Fragment schreibgeschützte Informationen anzeigt. Oder selbst wenn sie bearbeitbare Informationen anzeigen, verwenden Sie die Callback-Methoden, um die bearbeiteten Informationen beizubehalten.

  2. Unmittelbar nach dem Commit der Transaktion (Sie haben gerade commit() ), machen Sie einen Aufruf an FragmentManager.executePendingTransactions() .

Nicht empfohlene Wege:

  1. Wie Ovidiu Latcu bereits erwähnt hat, rufen Sie nicht super.onSaveInstanceState() . Aber das bedeutet, dass Sie den gesamten Zustand Ihrer Aktivität zusammen mit dem Zustand der Fragmente verlieren werden.

  2. Überschreiben Sie onBackPressed und rufen dort nur finish() . Dies sollte in Ordnung sein, wenn Ihre Anwendung die Fragmente-API nicht verwendet; wie in super.onBackPressed gibt es eine Aufforderung zur FragmentManager#popBackStackImmediate() .

  3. Wenn Sie die Fragmente-API verwenden und der Status Ihrer Aktivität wichtig ist, können Sie versuchen, die Reflection-API zu verwenden FragmentManagerImpl#noteStateNotSaved() . Aber das ist ein Hack, oder man könnte sagen, es ist ein Workaround. Ich mag es nicht, aber in meinem Fall ist es durchaus akzeptabel, da ich einen Code aus einer alten Anwendung habe, der veralteten Code verwendet ( TabActivity und stillschweigend LocalActivityManager ).

Nachfolgend der Code, der Reflexion 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);
    }
}

Zum Wohl!

38voto

FunkTheMonk Punkte 10881

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

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

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

27voto

Einfach anrufen super.onPostResume() bevor Sie Ihr Fragment anzeigen oder verschieben Sie Ihren Code in die Methode onPostResume() nach dem Aufruf von super.onPostResume(). Damit ist das Problem gelöst!

21voto

Brian Dilley Punkte 3698

Dies kann auch passieren, wenn Sie dismiss() auf ein Dialogfragment, nachdem der Bildschirm gesperrt wurde \blanked und der Zustand der Instanz des Dialogs Aktivität + wurde gespeichert. Um diesen Aufruf zu umgehen:

dismissAllowingStateLoss()

Buchstäblich jedes Mal, wenn ich einen Dialog entlasse, ist mir sein Zustand sowieso egal, also ist es in Ordnung, das zu tun - du verlierst nicht wirklich einen Zustand.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X