First, my app has structure like this:
SpashActivity -> MainActivity -> switching between many fragments
My app use SlideMenu to switch between fragments. I have to use attach
instead of replace
to keep fragment state. It's look like:
public void switchContent(int index, String fragmentTag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (fragmentManager.findFragmentByTag(fragmentTag) != mContent) {
if (!mContent.isDetached()) {
transaction.detach(mContent);
}
if (null == fragmentManager.findFragmentByTag(fragmentTag)) {
switch (index) {
case 0:
mContent = new CategoryFragment();
break;
case 1:
mContent = new BookFragment();
break;
case 2:
mContent = new BookDetailFragment();
break;
// etc
}
} else {
mContent = fragmentManager.findFragmentByTag(fragmentTag);
}
if (mContent.isDetached()) {
transaction.attach(mContent);
} else if (!mContent.isAdded()) {
transaction.add(R.id.content_frame, mContent, fragmentTag);
}
transaction.addToBackStack(null);
transaction.commit();
fragmentManager.executePendingTransactions();
}
Handler h = new Handler();
h.postDelayed(new Runnable() {
public void run() {
getSlidingMenu().showContent();
}
}, 50);
}
When the back stack count down to zero, my app has to exit:
@Override
public void onBackPressed() {
if (0 == getSupportFragmentManager().getBackStackEntryCount()) {
// show confirm exit dialog.
// If user press ok, try to exit, the splash activity will call finish()
// if(dialog == ok){
Intent intent = new Intent(MainActivity.this, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(SplashActivity.EXIT_KEY, true);
startActivity(intent);
// }
} else {
super.onBackPressed();
}
}
But sometimes (not always) I got the exception and crash my app when user press back many times (even the confirm exit dialog not appear - mean back stack count greater than zero):
09-30 20:32:53.419: E/AndroidRuntime(796): Uncaught handler: thread main exiting due to uncaught exception
09-30 20:32:53.470: E/AndroidRuntime(796): java.lang.RuntimeException: Unable to pause activity {com.org.scgroup/com.org.scgroup.MainActivity}: java.lang.IllegalStateException: Failure saving state: active BookDetailFragment{43e7e0d8} has cleared index: -1
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3162)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3119)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3102)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread.access$2400(ActivityThread.java:119)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1870)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.os.Handler.dispatchMessage(Handler.java:99)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.os.Looper.loop(Looper.java:123)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread.main(ActivityThread.java:4363)
09-30 20:32:53.470: E/AndroidRuntime(796): at java.lang.reflect.Method.invokeNative(Native Method)
09-30 20:32:53.470: E/AndroidRuntime(796): at java.lang.reflect.Method.invoke(Method.java:521)
09-30 20:32:53.470: E/AndroidRuntime(796): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
09-30 20:32:53.470: E/AndroidRuntime(796): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
09-30 20:32:53.470: E/AndroidRuntime(796): at dalvik.system.NativeStart.main(Native Method)
09-30 20:32:53.470: E/AndroidRuntime(796): Caused by: java.lang.IllegalStateException: Failure saving state: active BookDetailFragment{43e7e0d8} has cleared index: -1
09-30 20:32:53.470: E/AndroidRuntime(796): at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1716)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:532)
09-30 20:32:53.470: E/AndroidRuntime(796): at com.actionbarsherlock.app.SherlockFragmentActivity.onSaveInstanceState(SherlockFragmentActivity.java:126)
09-30 20:32:53.470: E/AndroidRuntime(796): at com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity.onSaveInstanceState(SlidingFragmentActivity.java:51)
09-30 20:32:53.470: E/AndroidRuntime(796): at com.org.scgroup.MainActivity.onSaveInstanceState(MainActivity.java:324)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.Activity.performSaveInstanceState(Activity.java:1022)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1180)
09-30 20:32:53.470: E/AndroidRuntime(796): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3144)
09-30 20:32:53.470: E/AndroidRuntime(796): ... 12 more
The onSaveInstanceState
method:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
try {
Ln.d("onSaveInstanceState");
outState.putInt(CURRENT_FRAGMENT_KEY, currentFragment);
if (0 < getSupportFragmentManager().getBackStackEntryCount()) {
getSupportFragmentManager().putFragment(outState, "mContent", mContent);
}
} catch (Exception ex) {
Ln.e(ex);
}
}
Could you please tell me where I am wrong? Thanks in advance.
The state of your fragments is automatically saved and restored, there is no need to do anything in your onSaveInstanceState
method. Don't hold any references to your Fragments in your Activity (in your case currentFragment
, mContent
), if you need a certain Fragment, get it from FragmentManager
by e.g. findFragmentByTag
.