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?
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();
}