15

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.

ductran
  • 10,043
  • 19
  • 82
  • 165
  • Did you find any solution for this problem? – Fahim Ahmed Nov 17 '13 at 11:17
  • 1
    No, I just figure out when I use replace instead of detaching and attaching fragment, this error isn't occurred. – ductran Nov 18 '13 at 04:48
  • I don't think it the solution, in my case, I make a replace transaction whick finish with this error too :/ – letroll Jul 07 '14 at 10:23
  • I am also facing similar issue. Have you got any solution for this. java.lang.IllegalStateException: Failure saving state: active fragment has cleared index: -1 If yes, then please post solution. – seema Apr 17 '15 at 07:53
  • Sorry, I still don't get the solution. If you look up the source code, you can find that fragment index in active fragment list is -1, but I don't know the root cause http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/support/v4/app/FragmentManager.java?av=f#1748 – ductran Apr 17 '15 at 13:38
  • I think you are over complicating the usage of fragments. Don't attach, detach and don't check if it is attached or detached. You can keep track of which fragment is currently shown to the user by saving it in a variable every time you use replace. Put every transaction on back stack and pop from back stack if you need to save its state. That should be it. – NecipAllef Oct 01 '15 at 06:44

3 Answers3

1

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.

artkoenig
  • 7,117
  • 2
  • 40
  • 61
1

Possible problem with reading fragment stat between detach and attach.

Try to use something like this:

FragmentTransaction ft = fragmentManager.beginTransaction();

//ft.detach(fragment).attach(fragment).commitAllowingStateLoss(); // crashing

ft.replace(getView().getId(), fragment).commitAllowingStateLoss(); // not crashing
Yury Finchenko
  • 1,035
  • 13
  • 19
0

Try to change

 @Override
public void onBackPressed() {
    if (0 == getSupportFragmentManager().getBackStackEntryCount()) {

to

 @Override
public void onBackPressed() {
    if (getSupportFragmentManager().getBackStackEntryCount() < 1 ) {

I hope this works

Ormoz
  • 2,975
  • 10
  • 35
  • 50
S. Alawadi
  • 144
  • 1
  • 6