如果我需要在 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 是否为真,如果是则删除带有提交的片段。这只是一个想法,可能需要加入更多的逻辑。
我很清楚什么是 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 是否为真,如果是则删除带有提交的片段。这只是一个想法,可能需要加入更多的逻辑。