我有一个托管 5 倍FragmentActivity
的TabActivity
。 其中一些包含按钮或列表,在其onClick()
或onItemClick()
中,创建和推送新片段。
在大多数情况下,这工作正常,但如果事情有点无响应,或者测试人员做了一些愚蠢的事情(按住按钮或列表项,使用不同的手指切换选项卡,然后释放按钮/列表 - 100% 可重现),我在活动暂停并保存后很好地获得了点击事件。 请参阅日志片段:
10-30 17:05:16.258 3415 3415 D BKC DEBUG: More.onSaveInstanceState()
10-30 17:05:16.258 3415 3415 D BKC DEBUG: MoreFragment.onPause()
10-30 17:05:17.309 3415 3415 D BKC DEBUG: MoreFragment.onItemClick()
在阅读了本文和有关片段状态丢失的各种 StackOverflow 问题后,我没有看到如何解决这个问题的好答案。
- 使用
commitAllowingStateLoss()
(无条件)是一种可以隐藏实际错误的解决方法。 - 我不确定在
onSaveInstanceState
中取消注册OnClickListener
s 和OnItemClickListener
s 会 100% 防止这种情况,并且为每个片段中的每个按钮执行此操作是一种 PITA。 - 有人建议检查相关片段的
isAdded()
,但我可以确认这不起作用。 - 我可以在
onSaveInstanceState()
和onRestoreInstanceState()
中设置一个标志,并在 onClick() 中检查它,但同样,这只是一个笨拙。 编辑:哦,片段没有onRestoreInstanceState()
,但我可以在onResume()
或其他什么地方摆动旗帜。
是否有我缺少的正确解决方案,或者我应该选择我选择的笨蛋?
再想一想,我相信commitAllowingStateLoss()
其实是这种情况的正确答案。 我们知道我们处于onClick()
或onItemClick()
处理程序中,因此如果可能发生状态丢失,我们知道这是因为这个漏洞允许点击事件在onSaveInstanceState()
之后通过。
在我的随意测试中,当您返回相关选项卡时,新片段实际上会弹出,因为实际上没有任何内容被拆除。 这对用户来说有点令人惊讶,但对于这种边缘情况来说可能是可以接受的。
我认为这里更优雅的解决方案是简单地忽略点击事件。在 BaseActivity 中设置一个非常简单的布尔标志,以便在活动暂停时进行跟踪:
class BaseActivity extends BaseFragmentActivity {
private boolean isPaused;
@Override
protected void onPause() {
isPaused = true;
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
isPaused = false;
}
public boolean isPaused() {
return isPaused;
}
}
每当您收到点击事件时,只需检查您的活动是否已暂停即可。如果是,那么忽略该事件是非常安全的,因为对其采取行动是没有意义的。
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
if (isPaused()) {
//But... we're paused. Ignore.
return;
}
//Act upon legitimate click events here
}
我可以确认这是支持库中的问题,而不是您的代码。最好的解决方法是(可能)使用@Doge给出的答案,即自己跟踪暂停状态。
支持库出现问题的原因是 onClicks 发生在点击发布之后。如果您使用多个手指,则可以在其他地方释放另一个点击事件(例如另一个按钮),从而更改片段。支持库在片段更改后获取其单击事件,并且不检查以确认它仍在前台。这意味着您必须自己检查。
这意味着,如果他们想要避免此崩溃,则对setCurrentTab()
或setCurrentTabByTag()
的任何调用都必须包括对暂停状态的检查。如果我错了,请纠正我。
由于活动已暂停,并且对于所选内容确实没有正确的操作,因此最好采取的操作可能是捕获并吃掉非法状态异常。让 UI 执行它正在执行的操作,并让 FragmentTransaction 落在地板上。