Search code examples
androidandroid-fragmentsbackground-processweak-references

Application crashes in background, when popping a fragment from stack


Application crashes, when I hit a server RPC, and when the RPC is in progress, I put the application in background. Meanwhile, when the RPC gets the response from server, it pops a fragment from stack. While popping the fragment, the application crashes. I have read about creating WeakReference, which will be null if the activity is destroyed. But not sure how to implement it this case.

Following is my code :

private void showFragment(SherlockFragment fragment) {
    FragmentManager fm = getSupportFragmentManager();
    fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    FragmentTransaction ft = fm.beginTransaction();
    ft.replace(R.id.content, fragment);
    ft.commit();
}

I get a crash on executing the following line:

fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

StackTrace :

01-15 16:37:44.435: E/AndroidRuntime(28049): FATAL EXCEPTION: main
01-15 16:37:44.435: E/AndroidRuntime(28049): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
01-15 16:37:44.435: E/AndroidRuntime(28049):    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299)
01-15 16:37:44.435: E/AndroidRuntime(28049):    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310)
01-15 16:37:44.435: E/AndroidRuntime(28049):    at android.support.v4.app.FragmentManagerImpl.popBackStack(FragmentManager.java:452)
01-15 16:37:44.435: E/AndroidRuntime(28049):    at com.druva.inSync.ValidationActivity$2.run(ValidationActivity.java:93)

Solution

  • I had the exact same problem, which I resolved using a flag. It may seem a little "hacky", but it does the job

    public abstract class PopActivity extends Activity {
    
        private boolean mVisible; 
    
       @Override
        public void onResume() {
            super.onResume();
            mVisible = true;
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mVisible = false;
        }
    
        private void popFragment() {
            if (!mVisible) {
                return;
            }
    
            FragmentManager fm = getSupportFragmentManager();
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
    

    So when you implement the above code alone when you resume the app you will find yourself in a fragment that you actually want to be popped. You can use the following snipped to fix this issue:

    public abstract class PopFragment extends Fragment {
    
        private static final String KEY_IS_POPPED = "KEY_IS_POPPED";
        private boolean mPopped;
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            outState.putBoolean(KEY_IS_POPPED, mPopped);
            super.onSaveInstanceState(outState);
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (savedInstanceState != null) {
                mPopped = savedInstanceState.getBoolean(KEY_IS_POPPED);
            }
        }
    
        @Override
        public void onResume() {
            super.onResume();
            if (mPopped) {
                popFragment();
            }
        }
    
        protected void popFragment() {
            mPopped = true;
            // null check and interface check advised
            ((PopActivity) getActivity()).popFragment();
        }
    }