Search code examples
androidandroid-fragmentsandroid-permissionsillegalstateexceptiononsaveinstancestate

IllegalStateException after revoking permissions


After the user revokes a permission in settings, and is brought back from the background, my app crashes with IllegalStateException: Can not perform this action after onSaveInstanceState. I see that the OS tries to recreate the fragment stack from the background (restarting the app will not result in crash). I have tried catching the revoke action with a flag, and if true just present the user with a dialog notifying to restart the app. But after showing the dialog the OS still plows ahead to try and recreate the stack, thus crashing.

I have also tried to pop all fragments if the flag is true, but no luck.

Google dev has stated that revoking permissions will lead to apps losing functionality, but a crash is way more than losing functionality. How do I suspend the app after the dialog is shown?


Solution

  • Revoking permissions is killing your undestroyed Activities, and when you return, they probably will be restored with savedInstanceState, and not the usual way you are testing.

    The Exception you posted means that your code performs commit() (or popBackStack()) on FragmentTransaction after onSaveInstanceState() (or onPause(), onStop(), which are usually called after), or as a result of some asynchronous operation, which you forgot to cancel when Activity got minimized.

    To stay safe, I usually track whether I can commit FragmentTransactions or not, like this

    public abstract class BaseActivity extends AppCompatActivity {
    
        private boolean mFragmentTransactionsAllowed;
    
        @Override
        protected void onCreate(@Nullable final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mFragmentTransactionsAllowed = true;
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            mFragmentTransactionsAllowed = true;
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mFragmentTransactionsAllowed = true;
        }
    
        @Override
        protected void onSaveInstanceState(final Bundle outState) {
            super.onSaveInstanceState(outState);
            mFragmentTransactionsAllowed = false;
        }
    
        protected final boolean areFragmentTransactionsAllowed() {
            return mFragmentTransactionsAllowed;
        }
    }
    

    And before committing I use to check

    if (areFragmentTransactionsAllowed()) {
        ft.commit();
    }