Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getSupportFragmentManager() cause java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

I was trying to figure out why:

getSupportFragmentManager().beginTransaction().commit();

fails, with

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

in a very basic FragmentActivity class.

So here was my use-case (this will be some pseudo-code and not a complete example, sorry): I had one FragmentActivity with an internal AsyncTask class. Roughly something like this:

public class HelloWorld extends FragmentActivity {
    showFragment(Fragment fragment, String name) {
        getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer, fragment, name).commit();
    }

    private class SlowFragmentShow extends AsyncTask<Context, String, Void> {
        protected Void doInBackground(Context... contexts) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                /* meh */
            }
        }

        protected void onPostExecute(Void nothing) {
            showFragment(new MyFragment(), "myFragment");
        }
    }
}

Or basically, 10 seconds after starting the app, it would show another fragment. Sounds simple, right? This seemed to work well too, until I decided to rotate the phone. When I did that, the app would crash upon calling "getSupportFragmentManager()..." with the "Can not perform this action...".

like image 867
Vidar Wahlberg Avatar asked Jun 14 '13 22:06

Vidar Wahlberg


People also ask

Can not perform this action after Onsaveinstancestate onBackPressed?

You can't call onBackPressed() when your activity is paused. However, the behavior on a back press is to leave the activity. Just call finish() instead of onBackPressed() . You should make sure in your onBackPressed() 's override that the activity is going to finish.

What is commitAllowingStateLoss?

You call commitAllowingStateLoss() after the application onStop() was called, the application process is killed by the system (low memory) while in background and the user returns back to your application.


1 Answers

After lots of debugging, it turned out that when SlowFragmentShow.onPostExecute() was called, which called showFragment(), which in turn called getSupportFragmentManager(), I received a FragmentManager that was in an IllegalState (so arguably the exception I got was correct). I'm still not sure why getSupportFragmentManager() would ever return an object in such a limbo state, but it did, and I needed to somehow get access to the "correct" FragmentManager. So to cut to the chase, I stored the FragmentManager as a static variable in my HelloWorld FragmentActivity, which I updated when HelloWorld.onStart() was called:

public class HelloWorld extends FragmentActivity {
    private static FragmentManager fragmentManager;

    public void onStart() {
        fragmentManager = getSupportFragmentManager();
        /* more code here */
    }

    showFragment(Fragment fragment, String name) {
        fragmentManager.beginTransaction().replace(R.id.fragmentContainer, fragment, name).commit();
    }

    private class SlowFragmentShow extends AsyncTask<Context, String, Void> {
        protected Void doInBackground(Context... contexts) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                /* meh */
            }
        }

        protected void onPostExecute(Void nothing) {
            showFragment(new MyFragment(), "myFragment");
        }
    }
}

And well, that pretty much fixed it. Now I could rotate the phone to my hearts desire, the fragment would still be shown when the AsyncTask was done.

In retrospect, it really seems a bit "oh, of course!", but the design decisions behind Android feels quite "alien" and unusual. I seem to end up with try-catch(Exception) around pretty much half the code just to prevent it from crashing on a non-fatal error (such as failing to update a text field), and a lot of static variables that needs to be updated upon onStart() because that seems like the only sane way you can reference Android objects without them being in an IllegalState.

like image 130
Vidar Wahlberg Avatar answered Oct 31 '22 12:10

Vidar Wahlberg