Android Fragment and orientation change causing: IllegalStateException: Can not perform this action after onSaveInstanceState

StackOverflow https://stackoverflow.com//questions/11684896

Question

I get this error whenever I have my main activity with a fragment loaded and the user starts a new activity, switches the orientation of the device and comes back to main activity.

@Override
public void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.home_layout);
    super.onCreate(savedInstanceState);
    fragmentManager = getSupportFragmentManager();
    fragment = fragmentManager.findFragmentById(R.id.layFragment);

    initialize();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    setContentView(R.layout.home_layout);
    initialize();
    super.onConfigurationChanged(newConfig);
}

private void initialize() {
    layStatus = (LinearLayout) findViewById(R.id.layStatus);
    txtStatus = (TextView) findViewById(R.id.txtStatus);
    ....
    handleFragments(lastFragmentId);
}

public void handleFragments(int fragmentId) {
        if (fragment == null) {
            FragmentTransaction ft = fragmentManager.beginTransaction();
            if (fragmentId==someFragmentId){
                ft.replace(R.id.layFragment, new FragmentSomeFragment());
            }
            else
            ....

            ft.commit();
        }
}

In my android manifest, the activity is declared as:

 <activity
        android:name=".HomeActivity"
        android:configChanges="keyboardHidden|orientation" />
<activity

In another questions here on SO, I have found that this may be caused by a bug in Support library do I added without any luck:

// needed as a workaround for a bug in the Support library
@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

My app runs from android 2.2 and I'm using the android-support-v4.jar Support library for fragments.

The log looks like:

07-27 11:56:20.399: E/AndroidRuntime(16021): FATAL EXCEPTION: main
07-27 11:56:20.399: E/AndroidRuntime(16021): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:541)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:525)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at com.rightcab.driver.core.HomeActivity.handleFragments(HomeActivity.java:341)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at com.rightcab.driver.core.HomeActivity.initialize(HomeActivity.java:128)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at com.rightcab.driver.core.HomeActivity.onConfigurationChanged(HomeActivity.java:153)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.app.ActivityThread.performConfigurationChanged(ActivityThread.java:3618)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.app.ActivityThread.handleActivityConfigurationChanged(ActivityThread.java:3771)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1328)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.os.Handler.dispatchMessage(Handler.java:99)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.os.Looper.loop(Looper.java:137)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at android.app.ActivityThread.main(ActivityThread.java:4745)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at java.lang.reflect.Method.invokeNative(Native Method)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at java.lang.reflect.Method.invoke(Method.java:511)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
07-27 11:56:20.399: E/AndroidRuntime(16021):    at dalvik.system.NativeStart.main(Native Method)
Was it helpful?

Solution

First, as I can see, you want to handle configuration changes yourself. In order to let things to work properly with API Level 13+, you have to add one more value to configChanges parameter, as described here.

Next, when the user leave your main activity, onSaveInstanceState and onPause methods are called for it. When the user rotates device and come back to your main activity. onConfigurationChanged method is called before onResume(). So, your activity is still paused and you cannot perform FragmentTransaction.

Further more, if we take a look into source code we can see the following comment for onResume method:

Dispatch onResume() to fragments. Note that for better inter-operation with older versions of the platform, at the point of this call the fragments attached to the activity are not resumed. This means that in some cases the previous state may still be saved, not allowing fragment transactions that modify the state. To correctly interact with fragments in their proper state, you should instead override {@link #onResumeFragments()}.

So, the right place to manipulate fragments in your activity is overriding onResumeFragments method, as we can read in the comment for this method in source code:

This is the fragment-orientated version of {@link #onResume()} that you can override to perform operations in the Activity at the same point where its fragments are resumed. Be sure to always call through to the super-class.

protected void onResumeFragments() {
    super.onResumeFragments();

    // YOUR STUFF IS HERE
}

OTHER TIPS

If as StenaviN suggests onConfigurationChange() is being returned before resume() on coming back to your activity:

This is the lifecycle:

onCreate()
onResume()
// Move away from you're Activity
onPause()
// Move back to your Activity
onConfigurationChange()
onResume()

But the important part is this:

If you resume an Activity or you change the Orientation of an Activity your Fragments will be fine! You don't need to replace the old one with a new copy and in fact you shouldn't! If you simply remove this line you won't have a problem:

handleFragments(lastFragmentId);

however If you're doing this because you need your Fragment to load a new layout resource (layout/frag.xml => layout-land/frag.xml) then you're going to need to do something like this:

boolean mResumed = false;
onPause() {
    mResumed = false;
}

onResume() {
    mResumed = true;
} 

...

    if(mResumed) handleFragments(lastFragmentId);

Are you using the latest version of the support-v4 library? It fixed my similar problem.

If you are stuck to the version r7 of support library (for instance because you are using maven and desesperatly waiting for an update.. ;) ), then you can use onPostResume to avoid this problem. If your version is r11 or above, then you can switch to onResumeFragements.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top