IllegalStateException: Can not perform this action after onSaveInstanceState with ViewPager
Please check my answer here. Basically I just had to :
@Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
Don't make the call to super()
on the saveInstanceState
method. This was messing things up...
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);
}
In the end the proper solution was (as seen in the comments) to use :
transaction.commitAllowingStateLoss();
when adding or performing the FragmentTransaction
that was causing the Exception
.
There are many related problems with a similar error message. Check the second line of this particular stack trace. This exception is specifically related to the call to FragmentManagerImpl.popBackStackImmediate
.
This method call, like popBackStack
, will always fail with IllegalStateException
if the session state has already been saved. Check the source. There is nothing you can do to stop this exception being thrown.
- Removing the call to
super.onSaveInstanceState
will not help. - Creating the Fragment with
commitAllowingStateLoss
will not help.
Here's how I observed the problem:
- There's a form with a submit button.
- When the button is clicked a dialog is created and an async process starts.
- The user clicks the home key before the process is finished -
onSaveInstanceState
is called. - The process completes, a callback is made and
popBackStackImmediate
is attempted. IllegalStateException
is thrown.
Here's what I did to solve it:
As it is not possible to avoid the IllegalStateException
in the callback, catch & ignore it.
try {
activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
// There's no way to avoid getting this if saveInstanceState has already been called.
}
This is enough to stop the app from crashing. But now the user will restore the app and see that the button they thought they'd pressed hasn't been pressed at all (they think). The form fragment is still showing!
To fix this, when the dialog is created, make some state to indicate the process has started.
progressDialog.show(fragmentManager, TAG);
submitPressed = true;
And save this state in the bundle.
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}
Don't forget to load it back again in onViewCreated
Then, when resuming, rollback the fragments if submit was previously attempted. This prevents the user from coming back to what seems like an un-submitted form.
@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;
}
}
Check if the activity isFinishing()
before showing the fragment and pay attention to commitAllowingStateLoss()
.
Example:
if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
DummyFragment dummyFragment = DummyFragment.newInstance();
ft.add(R.id.dummy_fragment_layout, dummyFragment);
ft.commitAllowingStateLoss();
}