如果我需要在 onNewIntent() 中或在 运行 时间更改后添加片段,如何避免 IllegalStateException?

How can I avoid an IllegalStateException if I need to add a Fragment in onNewIntent() or after a run-time change?

我很清楚什么是 IllegalStateException 以及当您在保存实例状态后尝试提交 FragmentTransactions 时为什么会发生这种情况。我已经通读了关于该主题以及“The Blog Post”的大多数流行的 Whosebug 问题。

我有一个特定的场景,我创建了一个片段来显示自定义进度微调器,而应用程序正在等待一些搜索结果出现。所以我的搜索 Activity 是 运行 在 singleTop 模式下并依赖 onNewIntent() 执行搜索并显示微调器。这一切都很好,但是当 运行 时间发生变化(比如方向变化)时失败。如果我在方向更改后尝试删除进度微调器片段,或者在语音搜索后添加微调器片段,我将得到 IllegalStateException。我的设置如下所示:

@Override
protected void onNewIntent(Intent intent) {
    if(intent.getAction().equals(Intent.ACTION_SEARCH)) {
        performSearch(intent.getStringExtra(SearchManager.QUERY));
    } 
}

performSearch() 方法将请求发送到我的服务器,然后像这样添加进度微调器片段...

private void showProgressSpinner() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.setCustomAnimations(R.anim.fragment_fade_in, R.anim.fragment_fade_out);
    ProgressFragment spinner = ProgressFragment.newInstance();
    transaction.add(R.id.searchContainer, spinner, "spinner");
    transaction.commit();
}

然后当搜索结果通过异步回调进来时,我像这样删除进度微调器片段...

private void dismissProgressSpinner() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.setCustomAnimations(R.anim.fragment_fade_in, R.anim.fragment_fade_out);
    ProgressFragment spinner = (ProgressFragment) fragmentManager.findFragmentByTag("spinner");
    if(spinner != null) {
        transaction.remove(spinner);
    }
    transaction.commit();
}

如果在显示进度微调器片段时方向发生变化,我会在搜索结果 return 时收到 IllegalStateException 并尝试删除微调器片段。根据 Alex Lockwood,将 FragmentTransaction 放在 onPostResume() 中会有所帮助,因为到那时您可以保证恢复状态。这确实有效,但我需要从搜索结果中删除我的异步回调中的片段,而不是在 Activity 恢复时。

所以我的问题是,如何在状态更改后但在我的异步回调中提交此片段事务?我试过使用 commitAllowingStateLoss() 但我仍然遇到异常,因为我的 spinner 片段仍在引用旧的已销毁 Activity。处理此问题的最佳方法是什么?

I've tried using commitAllowingStateLoss() but I still get the exception because my spinner Fragment is still referencing the old destroyed Activity.

您的片段是在更改配置后重新创建的,即。用户旋转屏幕后 - 您的微调器片段将被销毁并重新创建,并且在 onAttach 之后它将引用新的 activity 实例。此外,所有这些过程都是由 android 在 UI 线程上在单个消息中完成的,因此您的异步操作回调(也应在 UI 线程上执行)不可能在中执行中间.

我假设您没有在 ProgressFragment 中创建对 activity 的一些本地引用。

您可以编写额外的逻辑来确保您的提交在有效时刻被调用。 IE。在您的 activity onStart 中将一些静态布尔值 allowCommit 设置为 true,并在 onPause 中将其设置为 false。还要添加一些静态变量,searchWasFinished,并在您的异步回调中检查 allowCommit 是否为真,如果是,则立即删除微调器,如果不是,则仅将 searchWasFinished 设置为真。在您的 Activity.onStart 中检查 searchWasFinished==true 是否为真,如果是则删除带有提交的片段。这只是一个想法,可能需要加入更多的逻辑。