从onRequestPermissionsResult()中调用DialogFragment的show()会导致Marshmallow中的IllegalStateException

脚步:

  1. FragmentActivity请求许可
  2. onRequestPermissionsResult()显示DialogFragment
  3. 抛出java.lang.IllegalStateExceptiononSaveInstanceState后无法执行此操作

这是不会发生,当我显示对话后,延迟(使用postDelayed)。 根据http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html )我们可以在onPause()onStop()之间commit() onStop()没有任何状态丢失或EXCEPTION。 这里是一个示例项目源,日志文件和logging问题的链接。 https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing

另外我已经打开了一个问题https://code.google.com/p/android/issues/detail?id=190966,但它被标记为WorkingAsIntended,他们build议只是捕捉exception。 但是这并不能解决问题。 我知道解决它的其他方法,但不是这个android的bug?

更新 bug的状态再次被“分配”。 希望很快就能解决。 我的临时解决scheme是

 new Handler().postDelayed(new Runnable() { @Override public void run() { // do your fragment transaction here } }, 200); 

该错误被接受,将被修复,但是,我坚决不同意postDelayed和定时器解决scheme。 这样做的最好方法是在Activity中引入一个状态标志,在这个标志中设置callback,并在onResume或类似的地方使用。 例如:

 private boolean someFlag; public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // some code checking status someFlag = true; } 

然后在onResume:

 protected void onResume() { if(someFlag == true) { doSomething(); someFlag = false; } } 

我也认为这是Android的错误。 我无法相信他们标记了您的问题WorkingAsIntended 。 现在唯一的解决scheme是延迟执行onRequestPermissionsResult()的代码,直到android人正确地解决这个问题。

如果有人想知道如何延迟执行,这是我解决这个问题的方法:

   @Override public void onRequestPermissionsResult(int requestCode,String [] permissions,int [] grantResults){

     if(requestCode == PERMISSION_CODE){
      如果(/ *允许/拒绝* /){
          新的Timer()。schedule(new TimerTask(){
               @Override public void run(){
                   //执行行动(如碎片交易等)
               }
           },0);
       }
   }

这基本上延迟执行,直到onRequestPermissionsResult()完成,所以我们不会得到java.lang.IllegalStateException 。 这在我的应用程序中工作。

尝试这样的事情:

 // ... private Runnable mRunnable; @Override public void onResume() { super.onResume(); if (mRunnable != null) { mRunnable.run(); mRunnable = null; } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (/* PERMISSION_DENIED */) { mRunnable = /* new Runnable which show dialogFragment*/; } } 

这是Android中的一个错误https://code.google.com/p/android/issues/detail?id=190966&q=label%3AReportedBy-Developer&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars

所以,现在,解决方法是必要的。 你可以使用@ ldulcic的解决scheme。 或者你可以使用Handler的postDelay方法。 第二个选项是首选。

由于您使用的是DialogFragment ,因此您应该保留一个标志或状态,并在onPostResume显示对话框。 你应该在onPostResume而不是onResume或其他生命周期方法中做到这一点。

正如文档中明确指出的那样,如果您在Activity生命周期方法(而不是onPostResumeonResumeFragments (对于FragmentActivity))中提交事务,则在某些情况下可以在活动状态完全恢复之前调用该方法。

所以,如果你在PostResume上显示你的对话,你会没事的。

我相信这是一个更确定性的方法。 而不是使用计时器,我基本上排队的结果,直到Activity.onResumeFragments()(或者你可以在Activity.onResume()如果你不使用片段)。 我做了onResumeFragments(),因为我也将结果路由到请求权限的特定片段,所以我需要确保片段准备就绪。

这里是onRequestPermissionsResult():

 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // This is super ugly, but google has a bug that they are calling this method BEFORE // onResume... which screws up fragment lifecycle big time. So work around it. But also // be robust enough to still work if/when they fix the bug. if (mFragmentsResumed) { routePermissionsResult(requestCode, permissions, grantResults); } else { mQueuedPermissionGrantResults = grantResults; mQueuedPermissionRequestCode = requestCode; mQueuedPermissions = permissions; } } 

那么这里是onResumeFragments():

 @Override protected void onResumeFragments() { super.onResumeFragments(); mFragmentsResumed = true; if (mQueuedPermissionGrantResults != null) { routePermissionsResult(mQueuedPermissionRequestCode, mQueuedPermissions, mQueuedPermissionGrantResults); } 

为了完整起见,这里是onPause(),它清除了mFragmentsResumed标志:

 @Override protected void onPause() { super.onPause(); mFragmentsResumed = false; } 

我使用mFragmentsResumed标志,因为我不希望此代码停止工作,如果当谷歌修复了错误(改变生命周期调用的顺序将使代码不工作,如果我只是在onRequestPermissionsResult设置排队的variables,但onResumeFragments是在此之前叫)。