The following stack trace and exception message has plagued StackOverflow ever since Honeycomb's initial release:
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)
This post will explain why and when this exception is thrown, and will conclude with several suggestions that will help ensure it never crashes your application again.
Why was the exception thrown?
The exception was thrown because you attempted to commit a
FragmentTransaction after the activity's state had been saved, resulting in a phenomenon known as Activity state loss. Before we get into the details of what this actually means, however, let's first take a look at what happens under-the-hood when
onSaveInstanceState() is called. As I discussed in my last post about
Binder's & Death Recipients, Android applications have very little control over their destiny within the Android runtime environment. The Android system has the power to terminate processes at any time to free up memory, and background activities may be killed with little to no warning as a result. To ensure that this sometimes erratic behavior remains hidden from the user, the framework gives each Activity a chance to save its state by calling its
onSaveInstanceState() method before making the Activity vulnerable to destruction. When the saved state is later restored, the user will be given the perception that they are seamlessly switching between foreground and background activities, regardless of whether or not the Activity had been killed by the system.
When the framework calls
onSaveInstanceState(), it passes the method a
Bundle object for the Activity to use to save its state, and the Activity records in it the state of its dialogs, fragments, and views. When the method returns, the system parcels the
Bundle object across a Binder interface to the System Server process, where it is safely stored away. When the system later decides to recreate the Activity, it sends this same
Bundle object back to the application, for it to use to restore the Activity's old state.
So why then is the exception thrown? Well, the problem stems from the fact that these
Bundle objects represent a snapshot of an Activity at the moment
onSaveInstanceState() was called, and nothing more. That means when you call
onSaveInstanceState() is called, the transaction won't be remembered because it was never recorded as part of the Activity's state in the first place. From the user's point of view, the transaction will appear to be lost, resulting in accidental UI state loss. In order to protect the user experience, Android avoids state loss at all costs, and simply throws an
IllegalStateException whenever it occurs.