getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState" getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState" android android

getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"


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 :

@Overrideprotected 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 :

@Overrideprotected 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


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:

@Overrideprotected 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!


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.