it-swarm.com.de

IllegalStateException: Diese Aktion kann nach onSaveInstanceState mit ViewPager nicht ausgeführt werden

Ich erhalte Anwenderberichte von meiner App auf dem Markt und gebe 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 verwende. Der Stacktrace zeigt keine meiner eigenen Klassen an, daher weiß ich nicht, wo diese Ausnahme auftritt und wie sie verhindert werden kann.

Für das Protokoll: Ich habe einen Tabhost und in jeder Registerkarte gibt es eine ActivityGroup, die zwischen Aktivitäten wechselt.

414
nhaarman

Bitte überprüfen Sie meine Antwort hier . Im Grunde musste ich nur:

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

Rufen Sie nicht super() mit der saveInstanceState-Methode auf. Das war alles durcheinander ...

Dies ist ein bekanntes bug im Support-Paket. 

Wenn Sie die Instanz speichern und Ihrer outStateBundle etwas hinzufügen müssen, 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);
}

Am Ende war die richtige Lösung (wie in den Kommentaren zu sehen) zu verwenden:

transaction.commitAllowingStateLoss();

beim Hinzufügen oder Ausführen der FragmentTransaction, die die Exception verursacht hat.

656
Ovidiu Latcu

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 wird wie popBackStack always mit IllegalArgumentException fehlschlagen, wenn der Sitzungsstatus bereits gespeichert wurde. Überprüfen Sie die Quelle. Sie können nichts dagegen tun, dass diese Ausnahme ausgelöst wird. 

  • Das Entfernen des Anrufs an super.onSaveInstanceState hilft nicht. 
  • Das Erstellen des Fragments mit commitAllowingStateLoss hilft nicht.

So habe ich das Problem beobachtet:

  • Es gibt ein Formular mit einem Senden-Button.
  • Wenn Sie auf die Schaltfläche klicken, wird ein Dialogfeld erstellt und ein asynchroner Prozess gestartet.
  • Der Benutzer klickt auf den Home-Key, bevor der Prozess abgeschlossen ist - onSaveInstanceState wird aufgerufen.
  • Der Vorgang wird abgeschlossen, ein Rückruf wird ausgeführt und es wird versucht, popBackStackImmediate.
  • IllegalStateException wird geworfen.

So habe ich es gelöst:

Da es nicht möglich ist, die IllegalStateException im Callback zu vermeiden, fangen Sie sie ab und ignorieren Sie sie.

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

Dies reicht aus, um den Absturz der App zu verhindern. Nun stellt der Benutzer die App jedoch wieder her und stellt fest, dass die Schaltfläche, von der sie glaubten, dass sie gedrückt worden war, ü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.

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

Und speichern Sie diesen Status im Bundle.

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

Vergessen Sie nicht, es erneut in onViewCreated zu laden.

Wenn Sie den Vorgang fortsetzen, können Sie die Fragmente zurücksetzen, wenn zuvor ein Versuch ausgeführt wurde. Dies verhindert, dass der Benutzer zu einem scheinbar nicht gesendeten Formular zurückkehrt.

@Override
public void onResume() {
    super.onResume();
    if (submitPressed) {
        // no need to try-catch this, because we are not in a callback
        activity.getSupportFragmentManager().popBackStackImmediate(name);
        submitPressed = false;
    }
}
108
Synesso

Überprüfen Sie, ob die Aktivität isFinishing() angezeigt wird, bevor Sie das Fragment anzeigen, und achten Sie auf commitAllowingStateLoss().

Beispiel:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}
51
Naskov

Es ist Oktober 2017 und Google stellt Android Support Library mit der neuen Lifecycle-Komponente zur Verfügung. Es bietet eine neue Idee für das Problem "Kann diese Aktion nach onSaveInstanceState nicht ausführen".

Kurz gesagt:

  • Verwenden Sie die Lifecycle-Komponente, um zu bestimmen, ob der richtige Zeitpunkt für das Öffnen Ihres Fragments vorliegt.

Längere Version mit EXPLAIN:

  • warum ist dieses Problem aufgetreten?

    Das liegt daran, dass Sie versuchen, mit FragmentManager aus Ihrer Aktivität (die vermutlich Ihr Fragment enthält?) Eine Transaktion für Ihr Fragment durchzuführen. Normalerweise sieht es so aus, als würden Sie versuchen, eine Transaktion für ein bevorstehendes Fragment durchzuführen. Währenddessen ruft die Host-Aktivität bereits die Methode savedInstanceState auf (der Benutzer berührt möglicherweise die Home-Schaltfläche, sodass die Aktivität onStop() aufruft. , in meinem Fall ist es der Grund)

    Normalerweise sollte dieses Problem nicht auftreten - wir versuchen immer, Fragmente zu Beginn in die Aktivität zu laden, so wie die Methode onCreate() der perfekte Ort dafür ist. Aber manchmal passiert dies , insbesondere wenn Sie nicht entscheiden können, welches Fragment Sie in diese Aktivität laden möchten oder wenn Sie versuchen, ein Fragment aus einem AsyncTask blockieren (oder irgendetwas wird ein wenig Zeit in Anspruch nehmen). Die Zeit, bevor die Fragmenttransaktion wirklich stattfindet, aber nach der Methode onCreate() der Aktivität kann der Benutzer alles tun. Wenn der Benutzer die Home-Taste drückt, die die Methode onSavedInstanceState() der Aktivität auslöst, kommt es zu einem Absturz can not perform this action.

    Wenn jemand mehr über diese Ausgabe erfahren möchte, schlage ich vor, dass er sich diesen Blog ansieht post . Es schaut tief in die Quellcode-Ebene 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, es bietet nichts Gutes für Ihren Code).

  • Wie kann man das beheben?

    • Soll ich das Fragment mit der Methode commitAllowingStateLoss() laden? Nein, das solltest du nicht ;

    • Sollte ich die Methode onSaveInstanceState überschreiben, die darin enthaltene Methode super ignorieren? Nein, das solltest du nicht ;

    • Soll ich die magische isFinishing -Innentätigkeit verwenden, um zu überprüfen, ob die Host-Aktivität zum richtigen Zeitpunkt für eine Fragmenttransaktion vorliegt? Ja, das sieht aus wie der richtige Weg.

  • Sehen Sie sich an, was die Lebenszyklus Komponente kann.

    Grundsätzlich nimmt Google einige Implementierungen in der Klasse AppCompatActivity vor (und in mehreren anderen 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 würde dieses Problem auftreten? Es ist, weil wir etwas zum falschen Zeitpunkt tun. Also versuchen wir es nicht zu tun, und dieses Problem wird verschwunden sein.

    Ich programmiere ein wenig für mein eigenes Projekt. Hier ist, was ich mit LifeCycle mache. Ich codiere in Kotlin.

val hostActivity: AppCompatActivity? = null // the activity to Host fragments. It's value should be properly initialized.

fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
       if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
           showFragment(frag)
       }
    }
}

private fun showFragment(frag: Fragment) {
    hostActivity?.let {
        Transaction.begin(it, R.id.frag_container)
                .show(frag)
                .commit()
    }

Wie ich oben zeige. Ich überprüfe den Lebenszyklusstatus der Host-Aktivität. Mit der Lifecycle-Komponente in der Supportbibliothek könnte dies spezifischer sein. Der Code lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED) bedeutet, wenn der aktuelle Status mindestens onResume ist, nicht später als? Das stellt sicher, dass meine Methode nicht während eines anderen Lebenszustands (wie onStop) ausgeführt wird.

  • Ist alles erledigt?

    Natürlich nicht. Der Code, den ich gezeigt habe, zeigt einen neuen Weg, um das Abstürzen von Anwendungen zu verhindern. Wenn es sich jedoch um den Status onStop handelt, führt diese Codezeile keine Aktionen aus und zeigt daher nichts auf Ihrem Bildschirm an. Wenn Benutzer zur Anwendung zurückkehren, wird ein leerer Bildschirm angezeigt. Dies ist die leere Host-Aktivität, bei der überhaupt keine Fragmente angezeigt werden. Es ist eine schlechte Erfahrung (ja, ein bisschen besser als ein Absturz).

    Ich wünschte, es gäbe etwas Schöneres: Die App stürzt nicht ab, wenn der Lebensstatus später als onResume erreicht wird. Die Transaktionsmethode ist lebensstatusabhängig. Außerdem versucht die Aktivität, diese Fragmenttransaktionsaktion fortzusetzen, nachdem der Benutzer zu unserer App zurückgekehrt ist.

    Ich füge dieser Methode noch etwas hinzu:

class FragmentDispatcher(_Host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _Host
    private val lifeCycle: Lifecycle? = _Host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
        if (profilePendingList.isNotEmpty()) {
            showFragment(profilePendingList.last())
        }
    }

    fun dispatcherFragment(frag: BaseFragment) {
        if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
            showFragment(frag)
        } else {
            profilePendingList.clear()
            profilePendingList.add(frag)
        }
    }

    private fun showFragment(frag: BaseFragment) {
        hostActivity?.let {
            Transaction.begin(it, R.id.frag_container)
                    .show(frag)
                    .commit()
        }
    }
}

Ich führe eine Liste in dieser dispatcher - Klasse, um dieses Fragment zu speichern, habe ich keine Chance, die Transaktionsaktion abzuschließen. Wenn der Benutzer vom Startbildschirm zurückkehrt und feststellt, dass noch ein Fragment zum Starten bereitsteht, wechselt er zur Methode resume() unter der Annotation @OnLifecycleEvent(Lifecycle.Event.ON_RESUME). Jetzt denke ich, dass es funktionieren sollte, wie ich erwartet hatte.

21
Anthonyeef

Hier ist eine andere Lösung für dieses Problem.

Mit einer privaten Member-Variablen können Sie die zurückgegebenen Daten als Absicht festlegen, die dann nach super.onResume () verarbeitet werden kann.

So wie:

private Intent mOnActivityResultIntent = null; 

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}
21
Jed

Kurze und funktionierende Lösung:

Folgen Sie einfachen Schritten

Schritte

Schritt 1: Überschreiben Sie den onSaveInstanceState-Zustand im jeweiligen Fragment. Und super Methode daraus entfernen. 

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

Schritt 2: Verwenden Sie fragmentTransaction.commitAllowingStateLoss( ); 

anstelle von fragmentTransaction.commit( ); beim Fragmentieren von Operationen. 

15
Vinayak

VORSICHT, die Verwendung von transaction.commitAllowingStateLoss() könnte zu einer schlechten Erfahrung für den Benutzer führen. Weitere Informationen dazu, warum diese Ausnahme ausgelöst wird, finden Sie unter diesem Beitrag .

12
Eric Brandwein

Ich habe eine schmutzige Lösung für dieses Problem gefunden. Wenn Sie Ihre ActivityGroups aus welchem ​​Grund auch immer behalten möchten (ich hatte Zeitbegrenzungsgründe), implementieren Sie sie einfach

public void onBackPressed() {}

in deiner Activity und mache dort einen back-Code. Auch wenn auf älteren Geräten keine solche Methode vorhanden ist, wird diese Methode von neueren aufgerufen.

10
saberrider

Verwenden Sie nicht commitAllowingStateLoss (), es sollte nur für Fälle verwendet werden, in denen der UI-Status für den Benutzer unerwartet geändert werden kann.

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 () zur Überprüfung.

if (parentFragment.isResume()) {
    DummyFragment dummyFragment = DummyFragment.newInstance();
    transaction = childFragmentManager.BeginTransaction();
    trans.Replace(Resource.Id.fragmentContainer, startFragment);
}
6
Chandler

Ich hatte ein ähnliches Problem, das Szenario sah so aus:

  • Meine Aktivität ist das Hinzufügen/Ersetzen von Listenfragmenten.
  • Jedes Listenfragment hat einen Verweis auf die Aktivität, um die Aktivität zu benachrichtigen, wenn auf ein Listenelement geklickt wird (Beobachtermuster).
  • Jedes Listenfragment ruft setRetainInstance (true) auf; in seiner onCreate Methode.

Die onCreate Methode der Aktivität war wie folgt:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
mMainFragment.setOnSelectionChangedListener(this);
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }

Die Ausnahme wurde ausgelöst, da beim Ändern der Konfiguration (Gerät gedreht) die Aktivität erstellt wird, das Hauptfragment aus der Historie des Fragmentmanagers abgerufen wird und gleichzeitig das Fragment bereits eineOLD- Referenz hat die zerstörte Tätigkeit

durch die Änderung der Implementierung wurde das Problem gelöst:

mMainFragment = (SelectionFragment) getSupportFragmentManager()
                .findFragmentByTag(MAIN_FRAGMENT_TAG);
        if (mMainFragment == null) {
            mMainFragment = new SelectionFragment();

            mMainFragment.setListAdapter(new ArrayAdapter<String>(this,
                    R.layout.item_main_menu, getResources().getStringArray(
                            R.array.main_menu)));
            FragmentTransaction transaction = getSupportFragmentManager()
                    .beginTransaction();
            transaction.add(R.id.content, mMainFragment, MAIN_FRAGMENT_TAG);
            transaction.commit();
        }
        mMainFragment.setOnSelectionChangedListener(this);

Sie müssen Ihre Listener jedes Mal festlegen, wenn die Aktivität erstellt wird, um zu vermeiden, dass die Fragmente auf alte zerstörte Instanzen der Aktivität verweisen.

5
Mina Samy

Ich erhielt diese Ausnahme, als ich die Zurück-Taste drückte, um die Absichtsauswahl auf meiner Map-Fragment-Aktivität abzubrechen. Ich löste das, indem ich den Code von onResume (wo ich das Fragment initialisierte) in onstart () löste und die App funktioniert hoffentlich hilft es.

3
DCS

Wenn Sie FragmentTransaction in onActivityResult ausführen, können Sie einen booleschen Wert in onActivityResult setzen. In onResume können Sie Ihre FragmentTransaction auf der Grundlage des booleschen Werts ausführen. Bitte beziehen Sie sich auf den Code unten.

@Override
protected void onResume() {
    super.onResume;
    if(isSwitchFragment){
        isSwitchFragment=false;
        bottomNavigationView.getTabAt(POS_FEED).select();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == FilterActivity.FILTER_REQUEST_EVENT && data != null) {
        isSwitchFragment=true;
    }
}
2
anoopbryan2

Ich denke, die Verwendung von transaction.commitAllowingStateLoss(); ist nicht die beste Lösung. Diese Ausnahme wird ausgelöst, wenn die Konfiguration der Aktivität geändert wird und das Fragment onSavedInstanceState() aufgerufen wird. Anschließend versucht die async-Callback-Methode, ein Fragment festzuschreiben.

Eine einfache Lösung könnte überprüft werden, ob die Aktivität die Konfiguration ändert oder nicht

z.B. isChangingConfigurations() überprüfen

d.h.

if(!isChangingConfigurations()) { //commit transaction. } 

Checkout dieser Link ebenfalls

2
Amol Desai

Wenn Sie von FragmentActivity erben, müssen Sie die Superklasse in onActivityResult() aufrufen:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    ...
}

Wenn Sie dies nicht tun und versuchen, ein Fragmentdialogfeld in dieser Methode anzuzeigen, erhalten Sie möglicherweise die IllegalStateException von OP. (Um ehrlich zu sein, verstehe ich warum nicht ganz. Der Super-Aufruf behebt das Problem. onActivityResult() wird vor onResume() aufgerufen. Daher sollte es immer noch nicht möglich sein, ein Fragment-Dialogfeld anzuzeigen.

1

Im Hinblick auf die großartige Antwort von @Anthonyeef hier ein Beispielcode in Java:

private boolean shouldShowFragmentInOnResume;

private void someMethodThatShowsTheFragment() {

    if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
        showFragment();
    } else {
        shouldShowFragmentInOnResume = true;
    }
}

private void showFragment() {
    //Your code here
}

@Override
protected void onResume() {
    super.onResume();

    if (shouldShowFragmentInOnResume) {
        shouldShowFragmentInOnResume = false;
        showFragment();
    }
}
1
MorZa

Die wohl glatteste und einfachste Lösung, die ich in meinem Fall fand, bestand darin, zu vermeiden, dass das beleidigende Fragment als Reaktion auf das Aktivitätsergebnis vom Stapel genommen wird. So ändern Sie diesen Aufruf in meiner onActivityResult():

popMyFragmentAndMoveOn();

zu diesem:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}

hat in meinem Fall geholfen.

1
mojuba

Um dieses Problem zu umgehen, können wir The Navigation Architecture Component verwenden, das in Google I/O 2018 eingeführt wurde. Die Navigation Architecture Component vereinfacht die Implementierung der Navigation in einer Android-App. 

1
Levon Petrosyan

In meinem Fall habe ich diesen Fehler in einer Überschreibungsmethode namens onActivityResult erhalten. Nach dem Graben habe ich gerade herausgefunden, dass ich vorher 'super' anrufen musste.
Ich habe es hinzugefügt und es hat einfach funktioniert

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data); //<--- THIS IS THE SUPPER CALL
    if (resultCode == Activity.RESULT_OK && requestCode == 0) {
        mostrarFragment(FiltroFragment.newInstance())
    }

}

Möglicherweise müssen Sie nur einen 'Super'-Code hinzufügen, bevor Sie Ihren Code überschreiben.

1
Gian Gomen

Wenn Sie versuchen, ein Fragment in Ihre Aktivität zu laden, stellen Sie sicher, dass sich die Aktivität im Wiederaufnahmemodus befindet und nicht in den Pausezustand versetzt wird.

Sie können transaction.commitAllowingStateLoss () anstelle von transaction.commit () verwenden, um Fragment zu laden

oder

Erstellen Sie einen Booleschen Wert und prüfen Sie, ob die Aktivität nicht fortgesetzt wird

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

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

dann beim Laden der Fragmentprüfung

if(mIsResumed){
//load the your fragment
}
1
Anonymous

Courtesy: Lösung für IllegalStateException

Diese Ausgabe hatte mich lange Zeit geärgert, aber zum Glück hatte ich eine konkrete Lösung dafür. Eine ausführliche Erklärung dazu ist hier .

Die Verwendung von commitAllowStateloss () verhindert möglicherweise diese Ausnahmebedingung, führt jedoch zu Unregelmäßigkeiten der Benutzeroberfläche. Soweit wir verstanden haben, tritt IllegalStateException auf, wenn wir versuchen, ein Fragment festzulegen, nachdem der Aktivitätsstatus verloren gegangen ist Es kann einfach so gemacht werden

Deklarieren Sie zwei private boolesche Variablen

 public class MainActivity extends AppCompatActivity {

    //Boolean variable to mark if the transaction is safe
    private boolean isTransactionSafe;

    //Boolean variable to mark if there is any transaction pending
    private boolean isTransactionPending;

In onPostResume () und onPause setzen wir nun unsere boolesche Variable isTransactionSafe. Die Idee ist, trasnsaction nur dann als sicher zu kennzeichnen, wenn die Aktivität im Vordergrund steht, so dass keine Gefahr von Statusverlust besteht.

/*
onPostResume is called only when the activity's state is completely restored. In this we will
set our boolean variable to true. Indicating that transaction is safe now
 */
public void onPostResume(){
    super.onPostResume();
    isTransactionSafe=true;
}
/*
onPause is called just before the activity moves to background and also before onSaveInstanceState. In this
we will mark the transaction as unsafe
 */

public void onPause(){
    super.onPause();
    isTransactionSafe=false;

}

private void commitFragment(){
    if(isTransactionSafe) {
        MyFragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.frame, myFragment);
        fragmentTransaction.commit();
    }
}

- Was wir bisher getan haben, spart vor IllegalStateException, aber unsere Transaktionen gehen verloren, wenn sie nach dem Verschieben der Aktivität in den Hintergrund ausgeführt werden, ähnlich wie commitAllowStateloss (). Um dies zu unterstützen, haben wir die boolesche Variable isTransactionPending 

public void onPostResume(){
   super.onPostResume();
   isTransactionSafe=true;
/* Here after the activity is restored we check if there is any transaction pending from
the last restoration
*/
   if (isTransactionPending) {
      commitFragment();
   }
}


private void commitFragment(){

 if(isTransactionSafe) {
     MyFragment myFragment = new MyFragment();
     FragmentManager fragmentManager = getFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
     fragmentTransaction.add(R.id.frame, myFragment);
     fragmentTransaction.commit();
     isTransactionPending=false;
 }else {
     /*
     If any transaction is not done because the activity is in background. We set the
     isTransactionPending variable to true so that we can pick this up when we come back to
foreground
     */
     isTransactionPending=true;
 }
}
1
IrshadKumail

Fragmenttransaktionen sollten nicht nach Activity.onStop() ausgeführt werden! Stellen Sie sicher, dass Sie keine Rückrufe haben, die eine Transaktion nach onStop() ausführen könnten. Es ist besser, den Grund zu beheben, als zu versuchen, das Problem mit Ansätzen wie .commitAllowingStateLoss() zu umgehen.

0
Ivo Stoyanov

Ich weiß, es gibt eine akzeptierte Antwort von @Ovidiu Latcu, aber nach einiger Zeit bleibt der Fehler bestehen. 

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

Crashlytics sendet mir immer noch diese seltsame Fehlermeldung.

Allerdings tritt der Fehler jetzt nur bei Version 7+ (Nougat) auf Mein Fix bestand darin, commitAllowingStateLoss () anstelle von commit () bei fragmentTransaction zu verwenden. 

Dieses post ist hilfreich für commitAllowingStateLoss () und hatte nie wieder ein Fragmentproblem. 

Zusammenfassend lässt sich sagen, dass die akzeptierte Antwort hier auf Android-Versionen vor Nougat funktioniert. 

Dies kann jemandem ein paar Stunden der Suche ersparen. glückliche codierungen. <3 Prost

0
ralphgabb

verwenden Sie remove () anstelle von popup (), wenn der Status gespeichert ist.

   private void removeFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (fragmentManager.isStateSaved()) {
        List<Fragment> fragments = fragmentManager.getFragments();
        if (fragments != null && !fragments.isEmpty()) {
            fragmentManager.beginTransaction().remove(fragments.get(fragments.size() - 1)).commitAllowingStateLoss();
        }
    }
}
0
kim

Kotlin-Erweiterung

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    this?.findFragmentByTag(tag)?.let {
        beginTransaction().remove(it).commitNow()
    }
    // Add a fragment.
    this?.beginTransaction()?.run {
        replace(containerViewId, fragment, tag)
        // The next line will add the fragment to a back stack.
        // Remove if not needed.
        addToBackStack(null)
    }?.commitAllowingStateLoss()
}

Verwendungszweck:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)
0
CoolMind

Ich hatte genau das gleiche Problem . Es geschah aufgrund der Zerstörung der vorherigen Aktivität . Als die vorherige Aktivität gesichert wurde, wurde sie zerstört .

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SpinnerCustom2.setFragmentManager(getSupportFragmentManager());
    onCreateDrawerActivity(savedInstanceState);
}

Ich steckte es in Start, es war RECHTS

@Override
protected void onStart() {
    super.onStart();
    SpinnerCustom2.setFragmentManager(getSupportFragmentManager());

}
0
Samet öztoprak

Ein weiterer möglicher Workaround, bei dem ich nicht sicher bin, ob in allen Fällen (Origin hier ) hilft:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat) {
        final View rootView = findViewById(Android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}
0

Wenn Sie mit der Methode popBackStack () oder popBackStackImmediate () abgestürzt sind, versuchen Sie es mit fixt:

        if (!fragmentManager.isStateSaved()) {
            fragmentManager.popBackStackImmediate();
        }

Das funktioniert auch für mich.

0
Tapa Save

Am Ende habe ich ein Basisfragment erstellt und alle Fragmente in meiner App erweitern lassen

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

Wenn ich dann versuche, ein Fragment anzuzeigen, verwende ich showAllowingStateLoss anstelle von show

so was:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

Ich bin zu dieser Lösung aus dieser PR gekommen: https://github.com/googlesamples/easypermissions/pull/170/files

0
Ahmad El-Melegy

Die Ausnahme wird hier geworfen (In FragmentActivity):

@Override
public void onBackPressed() {
    if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
        super.onBackPressed();
    }
}

In FragmentManager.popBackStatckImmediate() first FragmentManager.checkStateLoss() wird zuerst aufgerufen. Das ist die Ursache von IllegalStateException. Siehe die Implementierung unten:

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

Ich löse dieses Problem einfach, indem ich den aktuellen Status von Activity mit einem Flag markiere. Hier ist meine Lösung:

public class MainActivity extends AppCompatActivity {
    /**
     * A flag that marks whether current Activity has saved its instance state
     */
    private boolean mHasSaveInstanceState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        mHasSaveInstanceState = true;
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mHasSaveInstanceState = false;
    }

    @Override
    public void onBackPressed() {
        if (!mHasSaveInstanceState) {
            // avoid FragmentManager.checkStateLoss()'s throwing IllegalStateException
            super.onBackPressed();
        }
    }
}
0
Frost Lau

@Gian Gomen In meinem Fall löst SUPER ein Problem. Es scheint eine korrektere Lösung zu sein als commitAllowingStateLoss (), da es das Problem löst und nicht versteckt.

@Override
public void onRequestPermissionsResult(
     final int requestCode,
     @NonNull final String[] permissions, 
     @NonNull final int[] grantResults
) {
        super.onRequestPermissionsResult(requestCode,permissions, grantResults); //<--- Without this line crash 
        switch (requestCode) {
            case Constants.REQUEST_CODE_PERMISSION_STORAGE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    onPermissionGranted(Constants.REQUEST_CODE_PERMISSION_STORAGE);
                }
                break;
        }

0
no_cola

Ich habe dieses Problem auch erlebt, und jedes Problem tritt auf, wenn der Kontext Ihrer FragmentActivity geändert wird (z. B. die Bildschirmausrichtung wird geändert usw.). Daher ist die beste Lösung das Aktualisieren des Kontexts von Ihrer FragmentActivity.

0
Adam

Fügen Sie dies in Ihre Aktivität ein

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (outState.isEmpty()) {
        // Work-around for a pre-Android 4.2 bug
        outState.putBoolean("bug:fix", true);
    }
}
0
yifan

Ab der Support Library Version 24.0.0 können Sie die FragmentTransaction.commitNow()-Methode aufrufen, die diese Transaktion synchron festschreibt, anstatt commit() gefolgt von executePendingTransactions() aufzurufen. Als Dokumentation sagt dieser Ansatz noch besser:

Das Aufrufen von commitNow ist dem Aufruf von commit () gefolgt von executePendingTransactions () vorzuziehen, da letzteres den Nebeneffekt hat, dass versucht wird, alle derzeit ausstehenden Transaktionen zu bestätigen, unabhängig davon, ob dies das gewünschte Verhalten ist oder nicht.