Issue
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...".
Solution
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
.
Answered By - Vidar Wahlberg
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.