Android 如果需要在onNewIntent()中或在运行时更改后添加片段,如何避免出现非法状态异常?

Android 如果需要在onNewIntent()中或在运行时更改后添加片段,如何避免出现非法状态异常?,android,android-fragments,illegalstateexception,Android,Android Fragments,Illegalstateexception,我很清楚什么是IllegalStateException,以及为什么在保存实例状态后尝试提交碎片事务时会发生这种情况。我已经通读了关于这个主题的大多数流行问题,以及“.” 我有一个特别的场景,在这个场景中,我创建了一个片段来显示一个定制的进度微调器,而应用程序正在等待一些搜索结果。因此,我的搜索活动是在单顶模式下运行的,依赖于onNewIntent()执行搜索并显示微调器。这一切都可以正常工作,但在运行时发生更改(如方向更改)时失败。如果我在方向更改后尝试删除进度微调器片段,或者在语音搜索后添加

我很清楚什么是IllegalStateException,以及为什么在保存实例状态后尝试提交碎片事务时会发生这种情况。我已经通读了关于这个主题的大多数流行问题,以及“.”

我有一个特别的场景,在这个场景中,我创建了一个片段来显示一个定制的进度微调器,而应用程序正在等待一些搜索结果。因此,我的搜索活动是在单顶模式下运行的,依赖于
onNewIntent()
执行搜索并显示微调器。这一切都可以正常工作,但在运行时发生更改(如方向更改)时失败。如果我在方向更改后尝试删除进度微调器片段,或者在语音搜索后添加微调器,我将获得非法状态异常。我的设置如下所示:

@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();
}
然后,当搜索结果通过一个异步回调传入时,我将删除进度微调器片段,如下所示

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();
}
如果在显示进度微调器片段时发生方向更改,则当搜索结果返回时,我会收到IllegalStateException,并尝试删除微调器片段。根据,将FragmentTransaction放入
onPostResume()
会有所帮助,因为您可以保证在那时恢复状态。这确实有效,但我需要从搜索结果中删除异步回调中的片段,而不是在活动恢复时

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

我已尝试使用commitAllowingStateLoss(),但仍然出现异常,因为我的微调器片段仍在引用旧的已销毁活动

您的片段是在配置更改后重新创建的,即在用户旋转屏幕后-您的微调器片段将被销毁并重新创建,并且在onAttach之后它将引用新的活动实例。同样,所有的进程都是通过Android在单个消息中的UI线程上完成的,所以你的异步操作回调(也应该在UI线程上执行)也没有机会在中间执行。 我假设您没有在ProgressFragment中创建一些对活动的本地引用


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

我实际上是在创建ProgressFragment内部活动的本地引用。我从
onAttach()
获取它,并使用它加载我的旋转动画。这是个问题吗?另外,关于你的“实例变量”建议,是的,这是我一直在使用的方法,但它只是让一切变得如此混乱和复杂…我希望有一个更简单的解决方案。不-我不这么认为,我在想,如果你可能泄露了活动参考,但只有当您的片段被保留并且您已经将活动的引用存储为类变量时,这才是可能的。我认为你应该能够在你的场景中使用commitAllowingStateLoss。其他解决方案相当混乱。当我使用
commitAllowingStateLoss()
时,它解决了一些问题(例如语音搜索时发生的状态丢失),但在方向更改时,当调用
dismissProgressSpinner()
时,我仍然会遇到错误,但是错误变为
java.lang.IllegalStateException:Activity已被销毁
——这让我非常困惑,因为我不知道为什么在
onResume()之后调用时会有对旧活动的引用
已完成加载保存的状态。请检查这是否是因为此错误:是的,我刚刚阅读了相关内容。我认为这只适用于嵌套片段。我只有一个片段被添加到活动中。