Search code examples
androidandroid-dialogfragmentandroid-6.0-marshmallow

Show DialogFragment when user clicks deny on Runtime permissions dialog


I'm trying to show a DialogFragment right after the user clicks "Deny" on the "allow/deny permission" dialog of android 6 But instead I'm getting this error:

    Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
  at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1448)
  at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1466)
  at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:634)
  at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:613)
  at android.support.v4.app.DialogFragment.show(DialogFragment.java:139)
  at android.support.v4.app.FragmentActivity.onRequestPermissionsResult(FragmentActivity.java:802)
  at android.app.Activity.dispatchRequestPermissionsResult(Activity.java:6553)
  at android.app.Activity.dispatchActivityResult(Activity.java:6432)
  at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
  at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
  at android.app.ActivityThread.-wrap16(ActivityThread.java) 
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
  at android.os.Handler.dispatchMessage(Handler.java:102) 
  at android.os.Looper.loop(Looper.java:148) 
  at android.app.ActivityThread.main(ActivityThread.java:5417) 
  at java.lang.reflect.Method.invoke(Native Method) 
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

My dialog code is pretty standard

  protected void showDialog(DialogFragment diag)
    {
        if (diag != null && !diag.isAdded())
            diag.show(getSupportFragmentManager(), "dialog");
    }

How can this be fixed?


Solution

  • So, there are a couple of solutions to this, none particularly pretty. What it means is that your Activity's FragmentManager is not yet in a valid state for committing FragmentTransactions in a safe manner (although in this particular case, it should always be.)

    1) Use .commitAllowingStateLoss() instead of .show():

    This is the easiest fix, although I'm not entirely clear what differences arise by not using the .show() methods, which set a few flags internally. Seems to work fine.

    getSupportFragmentManager()
            .beginTransaction()
            .add(diag, "dialog")
            .commitAllowingStateLoss();
    

    1) Post the .show() as a Runnable on a Handler:

    // Initialize a Handler ahead of time and keep it around
    private Handler mHandler = new Handler();
    
    ...
    
    // after getting denied:
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            // Show your dialog
        }
    }
    
    ...
    
    @Override
    protected void onStop() {
        super.onStop();
    
        // Clear out the Runnable for an unlikely edge case where your
        // Activity goes to stopped state before the Runnable executes
        mHandler.removeCallbacksAndMessages(null);
    }
    

    2) Set a flag and show the dialog in onResume()

    private boolean mPermissionDenied;
    
    @Override
    public void onRequestPermissionsResult(...) {
        // If denied...
        mPermissionDenied = true;   
    }
    
    @Override
    protected void onResume() {
        super.onResume();
    
        if (mPermissionDenied) {
            mPermissionDenied = false;
            // Show your dialog
        }
    }