从onRequestPermissionsResult()中调用DialogFragment的show()会导致Marshmallow中的IllegalStateException
脚步:
- 从
Fragment
或Activity
请求许可 - 在
onRequestPermissionsResult()
显示DialogFragment - 抛出
java.lang.IllegalStateException
:onSaveInstanceState
后无法执行此操作
这是不会发生,当我显示对话后,延迟(使用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*/; } }
所以,现在,解决方法是必要的。 你可以使用@ ldulcic的解决scheme。 或者你可以使用Handler的postDelay方法。 第二个选项是首选。
由于您使用的是DialogFragment
,因此您应该保留一个标志或状态,并在onPostResume
显示对话框。 你应该在onPostResume
而不是onResume
或其他生命周期方法中做到这一点。
正如文档中明确指出的那样,如果您在Activity生命周期方法(而不是onPostResume
或onResumeFragments
(对于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是在此之前叫)。