暂停时单击或项目单击会导致"IllegalStateException: Can not perform this action after onSaveInstanceState"



我有一个托管 5 倍FragmentActivityTabActivity。 其中一些包含按钮或列表,在其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 落在地板上。

最新更新