369

I have a Live Android application, and from market i have received following stack trace and i have no idea why its happening as its not happening in application code but its getting caused by some or the other event from the application (assumption)

I am not using Fragments, still there is a reference of FragmentManager. If any body can throw some light on some hidden facts to avoid this type of issue:

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
  • 4,039
  • 3
  • 16
  • 14
  • Did you find a solution yet? Having the same problem here: http://stackoverflow.com/questions/7575921/illegalstateexception-can-not-perform-this-action-after-onsaveinstancestate-ho – nhaarman Sep 27 '11 at 21:46
  • 1
    I had the same problem [and found a simple solution that works for me](http://stackoverflow.com/questions/11900785/illegalstateexception-fragment-support-library) – phlebas Aug 13 '12 at 09:45
  • 2
    @phlebas No you didn't. Yours concerns dialogs, and this does not. The top line of your stack trace matching is not enough. The rest is very different. I say this because I just went looking at your issue and it's no help to me unfortunately. – themightyjon Nov 07 '12 at 16:45
  • Do You use a Thread or AsynTask in that activity? – José Castro Mar 08 '13 at 05:55
  • 21
    I discuss this error in my [**blog post**](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html)... you should read it. :) – Alex Lockwood Aug 20 '13 at 23:18
  • If you press "Back" in your can @override (and put intent new activity and finish) recreate activity with all fragments. I try now and works –  Jul 14 '15 at 10:32
  • http://stackoverflow.com/questions/39292191/answer-to-java-lang-illegalstateexception-can-not-perform-this-action-after-ons Please refer this. – Ganesh Kanna Sep 02 '16 at 12:17
  • you get that crash because another app is in front and is dialog type and allows touches to be sent to your app. – chris Oct 04 '16 at 05:04

33 Answers33

468

This is the most stupid bug I have encountered so far. I had a Fragment application working perfectly for API < 11, and Force Closing on API > 11.

I really couldn't figure out what they changed inside the Activity lifecycle in the call to saveInstance, but I here is how I solved this :

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

I just do not make the call to .super() and everything works great. I hope this will save you some time.

EDIT: after some more research, this is a known bug in the support package.

If you need to save the instance, and add something to your outState Bundle you can use the following :

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

EDIT2: this may also occur if you are trying to perform a transaction after your Activity is gone in background. To avoid this you should use commitAllowingStateLoss()

EDIT3: The above solutions were fixing issues in the early support.v4 libraries from what I can remember. But if you still have issues with this you MUST also read @AlexLockwood 's blog : Fragment Transactions & Activity State Loss

Summary from the blog post (but I strongly recommend you to read it) :

  • NEVER commit() transactions after onPause() on pre-Honeycomb, and onStop() on post-Honeycomb
  • Be careful when committing transactions inside Activity lifecycle methods. Use onCreate(), onResumeFragments() and onPostResume()
  • Avoid performing transactions inside asynchronous callback methods
  • Use commitAllowingStateLoss() only as a last resort
Community
  • 1
  • 1
Ovidiu Latcu
  • 71,607
  • 15
  • 76
  • 84
  • This fixed it for me. I was using the ActionBar to control fragment navigation and got this problem when trying to reload the app. Adding a junk outstate fixed it. Thanks! – JMRboosties Aug 16 '12 at 19:34
  • 98
    You should use commitAllowingStateLoss() instead of commit() – meh Sep 28 '12 at 10:04
  • the onsavedinstancestate is already called before the commit() call is called, so the state you have saved there will be saved. – meh Oct 23 '12 at 11:17
  • 7
    So not calling super in onSaveInstanceState will stop the FragmentManager being able to save the state of all the fragments and restore them. That might cause issues with rotation. Also, I've just tried the other thing about putting junk in the bundle and that makes no difference for me. Not sure how it would - the bug you referenced in the support package is a NullPointerException, and doesn't seem much like this IllegalStateException... – themightyjon Nov 07 '12 at 16:48
  • Is it guaranteed that `outState` is not null? Because a NullPointerException may occur if that's the case...?! – t0mm13b Feb 28 '13 at 16:52
  • 57
    @meh `commitAllowingStateLoss()` only avoids the exception. It doesn't protect your application from accidental state loss. See this [**blog post**](http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html). – Alex Lockwood Aug 20 '13 at 23:20
  • 2
    @AlexLockwood so from that blog post we can learn that we should do all of our network calls inside the fragment(and display some temporary progress ui if needed) and that is the only way we can avoid getting this exception when it probably occurs because commit is being called after some asynchronous method call. – meh Aug 21 '13 at 21:30
  • @meh: at least that's how I am doing it. – gunar Sep 06 '13 at 08:36
  • @meh here i am commitAllowingStateLoss() but is some times works some time not can you tell me what is reason i am getting error unable to pause activity – Ando Masahashi Mar 21 '14 at 05:30
  • it causing me java.lang.IllegalStateException: Activity has been destroyed ! ..I am not using anty onSaveInsatance() even..any suggestion? – CoDe Aug 25 '14 at 12:29
  • I would strongly suggest to use `onPostResume` instead of `commitAllowingStateLoss` as it seems more proper and stable way. – Mehul Joisar Dec 10 '14 at 06:11
  • _+1_ for this the best answer http://stackoverflow.com/questions/16265733/failure-delivering-result-onactivityforresult/18345899#18345899 – nAkhmedov Mar 29 '15 at 07:54
  • 1
    I don't get the first point: "NEVER commit() transactions after ... onStop() on post-Honeycomb" . What if I need a button to trigger a fragment to be replaced with another? Should I put a boolean that checks if the activity has finished onStop, and if it does, call commitAllowingStateLoss instead? Also, what if I have a fragment within a fragment, that I need to replace upon click of a button? – android developer Sep 21 '15 at 10:13
  • @meh tnx , you saved me – aida Feb 09 '16 at 14:05
  • What about dialog fragments? How to commitAllowingStateLoss() while displaying fragment as a dialog. – VishalKale Apr 25 '16 at 07:28
  • For those who use DialogFragment, I've made an alternative to using this class, here: https://github.com/AndroidDeveloperLB/DialogShard – android developer Oct 22 '16 at 15:05
  • I got this exception when I call commit() in the onCreate. Any idea? – Kimi Chiu Nov 24 '16 at 04:54
  • In my case I was using "new Handler().postDelayed". After read this "Avoid performing transactions inside asynchronous callback methods. " I knew what was my problem. I commented the handler and the error has gone. Thanks to share a great article! – Jorge Casariego Mar 02 '17 at 17:40
  • can anyone help me in this http://stackoverflow.com/questions/43617600/illegal-state-exception-in-androidcan-not-perform-this-action-after-onsaveinsta – vm345 Apr 28 '17 at 18:26
  • @AlexLockwood : I've read your blog post. When the class extends **FragmentActivity** it is possible to override `FragmentActivity#onResumeFragments()` or `Activity#onPostResume()` but what's your solution about the situations which the class extends **Fragment** ? – Alireza Noorali Jul 03 '17 at 08:09
  • @Ovidiu what is the value for WORKAROUND_FOR_BUG_19917_KEY and value – Karan Sharma Jul 19 '17 at 06:51
  • Amazing, this really worked, and I am using static fragments. :) Thanks a lot. – Juan Mendez Oct 23 '17 at 07:09
  • /No call for super(). Bug on API Level > 11. If you do this, all your fragments won't be able to save theirs states. so this quick solution is counter productive. – Juan Mendez Nov 17 '17 at 12:14
  • we found that this post provides only alternate not a complete and proper answer and first solution is i think not working... – Rajan Bhavsar Jan 03 '19 at 10:17
  • According to the 36932872 issue it's fixed now. Is it now safe to avoid using the "WORKAROUND_FOR_BUG_19917_KEY " workaround? – android developer Dec 12 '19 at 12:35
76

Looking in Android source code on what causes this issue gives that flag mStateSaved in FragmentManagerImpl class (instance available in Activity) has value true. It is set to true when the back stack is saved (saveAllState) on call from Activity#onSaveInstanceState. Afterwards the calls from ActivityThread don't reset this flag using available reset methods from FragmentManagerImpl#noteStateNotSaved() and dispatch().

The way I see it there are some available fixes, depending on what your app is doing and using:

Good ways

Before anything else: I would advertise Alex Lockwood article. Then, from what I've done so far:

  1. For fragments and activities that don't need to keep any state information, call commitAllowStateLoss. Taken from documentation:

    Allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user`. I guess this is alright to use if the fragment is showing read-only information. Or even if they do show editable info, use the callbacks methods to retain the edited info.

  2. Just after the transaction is commit (you just called commit()), make a call to FragmentManager.executePendingTransactions().

Not recommended ways:

  1. As Ovidiu Latcu mentioned above, don't call super.onSaveInstanceState(). But this means you will lose the whole state of your activity along with fragments state.

  2. Override onBackPressed and in there call only finish(). This should be OK if you application doesn't use Fragments API; as in super.onBackPressed there is a call to FragmentManager#popBackStackImmediate().

  3. If you are using both Fragments API and the state of your activity is important/vital, then you could try to call using reflection API FragmentManagerImpl#noteStateNotSaved(). But this is a hack, or one could say it's a workaround. I don't like it, but in my case it's quite acceptable since I have a code from a legacy app that uses deprecated code (TabActivity and implicitly LocalActivityManager).

Below is the code that uses reflection:

@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);
    }
}

Cheers!

Community
  • 1
  • 1
gunar
  • 14,660
  • 7
  • 56
  • 87
  • This seems to happen with ActionBarSherlock too, under Gingerbread, so the case of checking Build Id seems moot... :( – t0mm13b Feb 28 '13 at 16:51
  • Also, you should point out - that is not suitable for ABS usage either :) – t0mm13b Feb 28 '13 at 17:08
  • @t0mm13b: The code above stands for my project as it doesn't use neither fragments, neither support.FragmentActivity. It runs on android.app.Activity and since the inconsistency that triggers the Exception is caused by FragmentManager (API level 11 upwards), that's why the check ... If you believe the source of evil is the same for you too, feel free to remove the check. ABS is a different story as it runs on top of compatibility package and the implementation of support.FragmentActivity may use the same implementation of FragmentManager and voila: same problem. – gunar Feb 28 '13 at 21:29
  • @t0mm13b: to add more as 600 chars are not enough, you have to investigate first what is really causing this for you. You have also to realize that above is an ugly hack and I am not taking any responsibility if it runs or not (for me was the best solution considering the circumstances). If you have to use it, double check in the compat source code for variable namings as those might differ from standard package. I would hope this issue would be tackled by next versions of compatibility package, but from Android experience, there are few chances to happen ... – gunar Feb 28 '13 at 21:34
  • Uhhh... Its exactly the same issue as in this [bug report](https://code.google.com/p/android/issues/detail?id=19917) *which* is the point of this OP's question. I stand by my comment - you **should** have explicitly put in a disclaimer and say it is not guaranteed and also **should** have stated that you did not use fragments - otherwise why bother posting that answer either! :) Just saying... – t0mm13b Feb 28 '13 at 21:50
  • It's a completely different issue. #19917 relates to a NPE, while this one to an IllegalStateException. Everyone is free to use any code from stackoverflow as I believe that is free and each developer MUST/SHOULD know what is he really doing :) – gunar Feb 28 '13 at 22:29
  • I don't see any reason why you would ever want to do all of that reflection stuff though... :) – Alex Lockwood Sep 15 '13 at 02:56
  • It was a stupid project that I took over and the prrvious dev overriden the ActivityManager behavior - believe it or not:) So this is another ugly hack I had to add there. I don't know if this code really belongs here :) ... but I'll leave it and each dev will judge if it's helpful or not – gunar Sep 15 '13 at 06:24
  • you answer is the best answer – wangzhengyi Nov 03 '16 at 07:20
  • can anyone help me in this http://stackoverflow.com/questions/43617600/illegal-state-exception-in-androidcan-not-perform-this-action-after-onsaveinsta – vm345 Apr 28 '17 at 18:27
  • Can someone pls explain what FragmentManager.executePendingTransactions() does exactly because of which it is the recommended way? – tom May 04 '21 at 14:50
38

Such an exception will occur if you try to perform a fragment transition after your fragment activity's onSaveInstanceState() gets called.

One reason this can happen, is if you leave an AsyncTask (or Thread) running when an activity gets stopped.

Any transitions after onSaveInstanceState() is called could potentially get lost if the system reclaims the activity for resources and recreates it later.

HitOdessit
  • 7,198
  • 4
  • 36
  • 59
FunkTheMonk
  • 10,908
  • 1
  • 31
  • 37
  • 2
    Hey Funk, I have a question here, how come the onBackPressed can be called on that activity or fragment, if that activity or fragment has been stopped. The above exception seems to be generated from some UI event (i.e. pressing of BACK key), I am not able to find out the relation between the Async Task and Back key though. – dcool Nov 09 '11 at 03:08
  • Since you can save fragment transitions to the back state, pressing the back button can cause the reverse of the transition you save (so old fragments come back). onSaveInstanceState is called before your activity is destroyed to restore resources to the system, not always after onStop is called. Sorry, that wasn't very clear in my answer. – FunkTheMonk Nov 09 '11 at 09:38
  • Funk, but I am not using any Fragments in my application. It might be the fragments used in native code. earlier I thought you were talking about the same. – dcool Nov 09 '11 at 10:33
  • Er... pass... do you extend Activity or FragmentActivity? – FunkTheMonk Nov 09 '11 at 10:35
  • my all classes are extending Activity. – dcool Nov 11 '11 at 04:36
  • 1
    I had an AsyncTask with a reference to a fragment. Problem solved after removing the super() call from onSaveInstanceState and replacing the reference from my AsyncTask with a WeakReference. – Buffalo Jun 28 '12 at 15:31
  • 2
    @Buffalo That's definitely not a solution to the problem. You should always call `super.onSaveInstanceState()`. – Alex Lockwood Aug 16 '13 at 15:27
  • Why will the system recreate activity later? – Kimi Chiu Nov 25 '16 at 03:31
27

Simply call super.onPostResume() before showing your fragment or move your code in onPostResume() method after calling super.onPostResume(). This solve the problem!

  • 6
    Calling onPostResume() ensures that onResumeFragments() gets called and to me this is the ideal solution. – j2emanue Jun 13 '14 at 16:19
21

This can also happen when calling dismiss() on a dialog fragment after the screen has been locked\blanked and the Activity + dialog's instance state has been saved. To get around this call:

dismissAllowingStateLoss()

Literally every single time I'm dismissing a dialog i don't care about it's state anymore anyway, so this is ok to do - you're not actually losing any state.

Brian Dilley
  • 3,888
  • 2
  • 24
  • 24
18

Short And working Solution :

Follow Simple Steps :

Step 1 : Override onSaveInstanceState state in respective fragment. And remove super method from it.

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

Step 2 : Use CommitAllowingStateLoss(); instead of commit(); while fragment operations.

fragmentTransaction.commitAllowingStateLoss();
Basbous
  • 3,927
  • 4
  • 34
  • 62
13

I think Lifecycle state can help to prevent such crash starting from Android support lib v26.1.0 you can have the following check:

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

or you can try:

Fragment.isStateSaved()

more information here https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved()

Metwalli
  • 1,861
  • 1
  • 18
  • 27
7

this worked for me... found this out on my own... hope it helps you!

1) do NOT have a global "static" FragmentManager / FragmentTransaction.

2) onCreate, ALWAYS initialize the FragmentManager again!

sample below :-

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
  • 121
  • 1
  • 1
6

I was always getting this when I tried to show fragment in onActivityForResult() method, so the problem was next:

  1. My Activity is paused and stopped, which means, that onSaveInstanceState() was called already (for both pre-Honeycomb and post-Honeycomb devices).
  2. In case of any result I made transaction to show/hide fragment, which causes this IllegalStateException.

What I made is next:

  1. Added value for determining if action I want was done (e.g. taking photo from camere - isPhotoTaken) - it can be boolean or integer value depending how much different transactions you need.
  2. In overriden onResumeFragments() method I checked for my value and after made fragment transactions I needed. In this case commit() was not done after onSaveInstanceState, as state was returned in onResumeFragments() method.
Oleg Novosad
  • 2,261
  • 1
  • 27
  • 28
5

I solved the issue with onconfigurationchanged. The trick is that according to android activity life cycle, when you explicitly called an intent(camera intent, or any other one); the activity is paused and onsavedInstance is called in that case. When rotating the device to a different position other than the one during which the activity was active; doing fragment operations such as fragment commit causes Illegal state exception. There are lots of complains about it. It's something about android activity lifecycle management and proper method calls. To solve it I did this: 1-Override the onsavedInstance method of your activity, and determine the current screen orientation(portrait or landscape) then set your screen orientation to it before your activity is paused. that way the activity you lock the screen rotation for your activity in case it has been rotated by another one. 2-then , override onresume method of activity, and set your orientation mode now to sensor so that after onsaved method is called it will call one more time onconfiguration to deal with the rotation properly.

You can copy/paste this code into your activity to deal with it:

@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);
} 
Ziem
  • 6,579
  • 8
  • 53
  • 86
douggynix
  • 141
  • 2
  • 2
4

My solution for that problem was

In fragment add methods:

@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();
}

May be bad, but couldn't find anything better.

Ziem
  • 6,579
  • 8
  • 53
  • 86
mc.dev
  • 2,675
  • 3
  • 21
  • 27
4

I had the same problem, getting IllegalStateException, but replacing all my calls to commit() with commitAllowingStateLoss() did not help.

The culprit was a call to DialogFragment.show().

I surround it with

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

and that did it. OK, I don't get to show the dialog, but in this case that was fine.

It was the only place in my app where I first called FragmentManager.beginTransaction() but never called commit() so I did not find it when I looked for "commit()".

The funny thing is, the user never leaves the app. Instead the killer was an AdMob interstitial ad showing up.

Roger C S Wernersson
  • 6,362
  • 6
  • 34
  • 45
  • 3
    Same here. I've solved Overriding the 'show(FragmentManager manager, String tag)' method, replacing 'commit' with 'commitAllowingStateLoss'; losing something because I can't set two private attributes of the Dialog: mDismissed and mShownByMe. But it seems to work every time :) – Francesco Ditrani Apr 28 '15 at 20:58
  • I've made an alternative solution to DialogFragment, that can avoid this exception : https://github.com/AndroidDeveloperLB/DialogShard – android developer Oct 25 '16 at 08:10
4

I got this issue.But I think this problem is not related to commit and commitAllowStateLoss.

The following stack trace and exception message is about 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)

But this exception was caused by onBackPressed()

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)

They were all caused by checkStateLoss()

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 will be true after onSaveInstanceState.

This problem rarely happens.I have never encountered this problem.I can not reoccurrence the problem.

I found issue 25517

It might have occurred in the following circumstances

  1. Back key is called after onSaveInstanceState, but before the new activity is started.

  2. use onStop() in code

I'm not sure what the root of the problem is. So I used an ugly way.

@Override
public void onBackPressed() {

    try{
        super.onBackPressed();
    }catch (IllegalStateException e){
        // can output some information here
        finish();
    }
}
Community
  • 1
  • 1
oO_ox
  • 41
  • 3
  • I didn't really solve the problem, but this problem is not related to commit and commitAllowStateLoss. – oO_ox Aug 17 '16 at 07:19
4

I have got the same issue in my App. I have been solved this issue just calling the super.onBackPressed(); on previous class and calling the commitAllowingStateLoss() on the current class with that fragment.

DanielBarbarian
  • 5,093
  • 12
  • 35
  • 44
Peter
  • 587
  • 6
  • 16
  • 2
    Thank you. This solution solved issue uisng `commitAllowingStateLoss()` instead of `commit()` – Chintak Patel Aug 21 '17 at 06:51
  • Avoid using commitAllowingStateLoss() https://medium.com/@elye.project/handling-illegalstateexception-can-not-perform-this-action-after-onsaveinstancestate-d4ee8b630066 – swooby Oct 05 '18 at 22:23
3

Another lifecycle way to solve the issue is to use the latest released lifecycle-ktx with kotlin.

lifecycleScope.launchWhenResumed {
    // your code with fragment or dialogfragment
}

The closure will be run after resume state, so even this method is called after stop, it'll be safly excuted when the next resume come.

You can also choose like

lifecycleScope.launchWhenCreated
// or
lifecycleScope.launchWhenStarted

to fit your situation.

The code will be cancelled when destroy is come.

The Google document link: https://developer.android.com/kotlin/ktx#lifecycle

royorange
  • 31
  • 3
3

onSaveInstance will be called if a user rotates the screen so that it can load resources associated with the new orientation.

It's possible that this user rotated the screen followed by pressing the back button (because it's also possible that this user fumbled their phone while using your app)

dols
  • 733
  • 8
  • 16
2

Read http://chris-alexander.co.uk/on-engineering/dev/android-fragments-within-fragments/

article. fragment.isResumed() checking helps me in onDestroyView w/o using onSaveInstanceState method.

andep
  • 8,033
  • 1
  • 13
  • 5
2

Same issue from me and after a day long analysis of all articles, blog and stackoverflow i've found a simple solution. Don't use savedInstanceState at all, this is the condition with one line of code. On the fragment code:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
    .....
sKhan
  • 9,694
  • 16
  • 55
  • 53
2

This happens whenever you are trying to load a fragment but the activity has changed its state to onPause().This happens for example when you try to fetch data and load it to the activity but by the time the user has clicked some button and has moved to next activity.

You can solve this in two ways

You can use transaction.commitAllowingStateLoss() instead of transaction.commit() to load fragment but you may end up losing commit operation that is done.

or

Make sure that activity is in resume and not going to pause state when loading a fragment. Create a boolean and check if activity is not going to onPause() state.

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

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

then while loading fragment check if activity is present and load only when activity is foreground.

if(mIsResumed){
 //load the fragment
}
Sharath kumar
  • 4,064
  • 1
  • 14
  • 20
1

Thanks @gunar, but I think there is a better way.

According to 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();

So use commitNow to replace:

fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
JianxinLi
  • 23
  • 4
0

This is fixed in Android 4.2 and also in the support library's source.[*]

For details of the cause (and work-arounds) refer to the the Google bug report: http://code.google.com/p/android/issues/detail?id=19917

If you're using the support library then you shouldn't have to worry about this bug (for long)[*]. However, if you're using the API directly (i.e. Not using the support library's FragmentManager) and targeting an API below Android 4.2 then you will need to try one of the work-arounds.

[*] At the time of writing the Android SDK Manager is still distributing an old version that exhibits this bug.

Edit I'm going to add some clarification here because I've obviously somehow confused whoever down-voted this answer.

There are several different (but related) circumstances that can cause this exception to be thrown. My answer above is referring to the specific instance discussed in the question i.e. a bug in Android which has subsequently been fixed. If you're getting this exception for another reason it's because you're adding/removing fragments when you shouldn't be (after fragment states have been saved). If you're in such a situation then perhaps "Nested Fragments - IllegalStateException “Can not perform this action after onSaveInstanceState”" can be of use to you.

Community
  • 1
  • 1
Benjamin Dobell
  • 4,042
  • 1
  • 32
  • 44
0

Well, after trying all the above solutions without success (because basically i dont have transactions).

On my case i was using AlertDialogs and ProgressDialog as fragments that, sometimes, on rotation, when asking for the FragmentManager, the error rises.

I found a workaround mixing some many similar posts:

Its a 3 step solution, all done on your FragmentActivity (in this case, its called 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
  • 340
  • 2
  • 10
0

When i use startactivity in one fragment, i will get this exception;

When i change to use startactivityforresult, the exception is gone :)

So the easy way to fix it is use the startActivityForResult api :)

Question
  • 61
  • 1
  • 3
0

I was getting this exception when I was pressing back button to cancel intent chooser on my map fragment activity. I resolved this by replacing the code of onResume()(where I was initializing the fragment and committing transaction) to onStart() and the app is working fine now. Hope it helps.

DevChamp
  • 188
  • 2
  • 2
  • 14
0

After researching a bit the solution to this problem is to do your fragment commits in the onresume.

Source: https://wenchaojames.wordpress.com/2013/01/12/illegalstateexception-from-onactivityresult/

jai
  • 321
  • 3
  • 9
0

My use case: I have used listener in fragment to notify activity that some thing happened. I did new fragment commit on callback method. This works perfectly fine on first time. But on orientation change the activity is recreated with saved instance state. In that case fragment is not created again implies that the fragment have the listener which is old destroyed activity. Any way the call back method will get triggered on action. It goes to destroyed activity which cause the issue. The solution is to reset the listener in fragment with current live activity. This solve the problem.

Ganesh Kanna
  • 2,269
  • 1
  • 19
  • 29
0

What I found is that if another app is dialog type and allows touches to be sent to background app then almost any background app will crash with this error. I think we need to check every time a transaction is performed if the instance was saved or restored.

chris
  • 313
  • 2
  • 12
0

In my case, with the same error exception, i put the "onBackPressed()" in a runnable (you can use any of your view):

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

I do not understand why, but it works!

Alecs
  • 2,900
  • 1
  • 22
  • 25
  • Posting on a view will only run the Runnable after the view has been properly laid out and drawn to the screen; this often means the Activity itself has fully resumed, hence no issues – Mercato Dec 04 '17 at 16:18
0

You may be calling fragmentManager.popBackStackImmediate(); when activity is paused. Activity is not finished but is paused and not on foreground. You need to check whether activity is paused or not before popBackStackImmediate().

Murat
  • 3,084
  • 37
  • 55
0

I noticed something very interesting. I have in my app the option to open the phone's gallery and the device asks what app to use, there I click on the gray area away from the dialog and saw this issue. I noticed how my activity goes from onPause, onSaveInstanceState back to onResume, it doesn't happen to visit onCreateView. I am doing transactions at onResume. So what I ended up doing is setting a flag being negated onPause, but being true onCreateView. if the flag is true onResume then do onCommit, otherwise commitAllowingStateLoss. I could go on and waste so much time but I wanted to check the lifecycle. I have a device which is sdkversion 23, and I don't get this issue, but I have another one which is 21, and there I see it.

Juan Mendez
  • 2,658
  • 1
  • 27
  • 23
0

When a Fragment or AppCompatActivity's state is saved via onSaveInstanceState(), it's UI is considered immutable until ON_START is called. Trying to modify the UI after the state is saved is likely to cause inconsistencies in the navigation state of your application which is why FragmentManager throws an exception if the app runs a FragmentTransaction after state is saved. See commit() for details.

LiveData prevents this edge case out of the box by refraining from calling its observer if the observer's associated Lifecycle isn't at least STARTED. Behind the scenes, it calls isAtLeast() before deciding to invoke its observer.

-1

you can use FragmentActivity.onStart before popBackStackImmediate

like this:

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

benka
  • 4,732
  • 35
  • 47
  • 58
-2

I think calling FragmentActivity.onStateNotSaved() before those operations could be the best option by now.

Raptor
  • 29
  • 3