步骤:
Fragment
或Activity
onRequestPermissionsResult()
java.lang.IllegalStateException
:在onSaveInstanceState
当我在延迟一段时间后显示对话框时(使用postDelayed),这不会发生。
根据{{3}})关于后蜂窝设备,我们commit()
和onPause()
之间onStop()
可以没有任何状态损失或异常。
这是一个示例项目源,日志文件和记录问题的链接。
http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
此外,我已经打开了一个问题https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing,但它被标记为WorkingAsIntended,他们建议只捕获异常。但这并没有解决问题。我知道解决它的其他方法,但不是这个安卓漏洞吗?
更新 该错误的状态再次被分配"。希望很快就能解决。 我的临时解决方案是
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// do your fragment transaction here
}
}, 200);
答案 0 :(得分:24)
该错误已被接受并将被修复,但是,我强烈反对postDelayed和计时器解决方案。执行此操作的最佳方法是在Activity中引入状态标志,您可以在其中设置回调,并使用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;
}
}
答案 1 :(得分:6)
我也认为这是android bug。我无法相信他们标记了您的问题 WorkingAsIntended 。目前唯一的解决方案是延迟onRequestPermissionsResult()
中代码的执行,直到android人员正确修复此问题。
如果有人想知道如何延迟执行,这是我解决这个问题的方法:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == PERMISSION_CODE) { if (/* PERMISSION ALLOWED/DENIED */) { new Timer().schedule(new TimerTask() { @Override public void run() { // EXECUTE ACTIONS (LIKE FRAGMENT TRANSACTION ETC.) } }, 0); } }
这实质上会延迟执行,直到onRequestPermissionsResult()
完成,因此我们无法获得java.lang.IllegalStateException
。这适用于我的应用程序。
答案 2 :(得分:4)
尝试这样的事情:
// ...
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*/;
}
}
答案 3 :(得分:4)
所以,目前,解决方法是必要的。您可以使用@ ldulcic的解决方案。或者您可以使用Handler的postDelay方法。第二种选择是首选。
答案 4 :(得分:4)
由于您使用的是DialogFragment
,因此您应该保留一个标记或状态,而不是在onPostResume
中显示您的对话框。您应该在onPostResume
而不是onResume
或其他生命周期方法中执行此操作。
正如documentation明确指出的那样,如果您在除onPostResume
或onResumeFragments
之外的Activity生命周期方法中提交事务(对于FragmentActivity),在某些情况下,可以在活动的状态已经完全恢复。
所以,如果你在PostResume上显示你的对话框,你会没事的。
答案 5 :(得分:2)
我认为这是一种更确定的方式。我没有使用计时器,而是将结果排队,直到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中,但在此之前调用了onResumeFragments。
答案 6 :(得分:0)
正如issue中的一条评论所解释的那样,只有在使用支持库with open("file", "rb") as infile, open('output', 'wb') as outfile:
buf = bytearray()
for n in randoms:
infile.seek(lsize * n)
buf.extend(infile.read(lsize))
outfile.write(buf)
(即DialogFragment
)时才会发生这种情况。您可以更改代码以使用" native" android.support.v4.app.DialogFragment
(DialogFragment
),它会正常工作。
我知道在某些情况下使用原生android.app.DialogFragment
是不可能的,但在这种情况下,鉴于您指的是运行时权限(仅在最新版本的Android中可用的功能),最有可能做到这一点。我有这个问题,我可以这样解决。
答案 7 :(得分:0)
我也使用Handler解决了这个问题,但是我避免使用计时器将其Looper设置为MainLooper。因此它将在呈现视图后运行:
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// do your fragment transaction here
}
});