Android Fragment 和方向更改导致:IllegalStateException: Can not perform this action after onSaveInstanceStat
Posted
技术标签:
【中文标题】Android Fragment 和方向更改导致:IllegalStateException: Can not perform this action after onSaveInstanceState【英文标题】:Android Fragment and orientation change causing: IllegalStateException: Can not perform this action after onSaveInstanceState 【发布时间】:2012-07-25 23:02:57 【问题描述】:每当我加载了一个片段的主要活动并且用户启动一个新活动、切换设备的方向并返回到主要活动时,我都会收到此错误。
@Override
public void onCreate(Bundle savedInstanceState)
setContentView(R.layout.home_layout);
super.onCreate(savedInstanceState);
fragmentManager = getSupportFragmentManager();
fragment = fragmentManager.findFragmentById(R.id.layFragment);
initialize();
@Override
public void onConfigurationChanged(Configuration newConfig)
setContentView(R.layout.home_layout);
initialize();
super.onConfigurationChanged(newConfig);
private void initialize()
layStatus = (LinearLayout) findViewById(R.id.layStatus);
txtStatus = (TextView) findViewById(R.id.txtStatus);
....
handleFragments(lastFragmentId);
public void handleFragments(int fragmentId)
if (fragment == null)
FragmentTransaction ft = fragmentManager.beginTransaction();
if (fragmentId==someFragmentId)
ft.replace(R.id.layFragment, new FragmentSomeFragment());
else
....
ft.commit();
在我的 android 清单中,活动声明为:
<activity
android:name=".HomeActivity"
android:configChanges="keyboardHidden|orientation" />
<activity
在关于 SO 的另一个问题中,我发现这可能是由 Support 库中的错误引起的,我是否添加了任何运气:
// needed as a workaround for a bug in the Support library
@Override
protected void onSaveInstanceState(Bundle outState)
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
我的应用从 android 2.2 运行,我正在使用 android-support-v4.jar 支持库来支持片段。
日志如下:
07-27 11:56:20.399: E/AndroidRuntime(16021): FATAL EXCEPTION: main
07-27 11:56:20.399: E/AndroidRuntime(16021): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1299)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1310)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:541)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:525)
07-27 11:56:20.399: E/AndroidRuntime(16021): at com.rightcab.driver.core.HomeActivity.handleFragments(HomeActivity.java:341)
07-27 11:56:20.399: E/AndroidRuntime(16021): at com.rightcab.driver.core.HomeActivity.initialize(HomeActivity.java:128)
07-27 11:56:20.399: E/AndroidRuntime(16021): at com.rightcab.driver.core.HomeActivity.onConfigurationChanged(HomeActivity.java:153)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.app.ActivityThread.performConfigurationChanged(ActivityThread.java:3618)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.app.ActivityThread.handleActivityConfigurationChanged(ActivityThread.java:3771)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1328)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.os.Handler.dispatchMessage(Handler.java:99)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.os.Looper.loop(Looper.java:137)
07-27 11:56:20.399: E/AndroidRuntime(16021): at android.app.ActivityThread.main(ActivityThread.java:4745)
07-27 11:56:20.399: E/AndroidRuntime(16021): at java.lang.reflect.Method.invokeNative(Native Method)
07-27 11:56:20.399: E/AndroidRuntime(16021): at java.lang.reflect.Method.invoke(Method.java:511)
07-27 11:56:20.399: E/AndroidRuntime(16021): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
07-27 11:56:20.399: E/AndroidRuntime(16021): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
07-27 11:56:20.399: E/AndroidRuntime(16021): at dalvik.system.NativeStart.main(Native Method)
【问题讨论】:
【参考方案1】:首先,如我所见,您希望自己处理配置更改。为了让 API 级别 13+ 正常工作,您必须向 configChanges
参数添加一个值,如 here 所述。
接下来,当用户离开您的主要活动时,会为此调用 onSaveInstanceState
和 onPause
方法。当用户旋转设备并回到您的主要活动时。 onConfigurationChanged
方法被调用之前 onResume()
。因此,您的活动仍处于暂停状态,您无法执行FragmentTransaction
。
此外,如果我们查看源代码,我们可以看到onResume
方法的以下注释:
分派 onResume() 到片段。请注意,为了更好 与旧版本平台的互操作,在点 这个调用附加到活动的片段是不是 恢复。这意味着在某些情况下,之前的状态可能仍然 被保存,不允许修改状态的片段事务。 要正确与处于正确状态的片段交互,您应该 而是覆盖 @link #onResumeFragments()。
因此,在您的 Activity 中操作片段的正确位置是覆盖 onResumeFragments
方法,正如我们可以在源代码中对该方法的注释中看到的那样:
这是面向片段的 @link #onResume() 版本 您可以覆盖以同时在 Activity 中执行操作 恢复其片段的点。一定要经常打电话 到超类。
protected void onResumeFragments()
super.onResumeFragments();
// YOUR STUFF IS HERE
【讨论】:
我无法使用 onResumeFragments,因为它在我的 Activity 中不可用。也许是因为我正在针对 SDK 2.2 构建?我尝试在活动的 onResume 中添加 handleFragments() 但结果仍然相同。有什么想法吗? @Alin,如果您使用片段,您的活动应该来自FragmentActivity
,并且您的项目应该引用 v4 兼容性库。 onResumeFragments 是 FragmentActivity
的受保护方法,您可以覆盖它!
你说得对,我一定是写错了,我又试了一遍,现在我有了 onResumeFragments()。我之前提出的场景现在似乎可以工作了:onResumeFragments 我调用了 handleFragments() 并且没有错误,并且片段的布局根据当前方向发生了变化。但是,当我在屏幕上使用片段进行主要活动并更改方向时,我的 handleFragmens 不会在任何地方被调用,因此我留在片段应该位于的空白位置。那么......我应该如何处理 onConfigurationChanged 中的 handleFragment ?
@Alin,正如我所见,您根据当前方向替换片段。那么,自己处理方向变化的原因是什么? @Alin,是的,当您旋转位于主片段上的设备时,onPause
、onResume
、onPostResume
和 onResumeFragments
方法不会被调用,因为您说 Android:“我会自己处理配置更改!”。我建议你去掉AndroidManifest.xml
中的configChanges
参数,并在onCreate
方法中设置新的布局。
其实我想我想通了:如果我在 onPause 中创建一个标志并且知道应用程序已暂停,那么不要在 onConfigurationChanged 中使用 handleFragments()。现在它似乎工作了,将进行一些进一步的测试【参考方案2】:
如果 StenaviN 建议 onConfigurationChange()
在 resume() 之前返回您的活动:
这是生命周期:
onCreate()
onResume()
// Move away from you're Activity
onPause()
// Move back to your Activity
onConfigurationChange()
onResume()
但重要的是:
如果您恢复Activity
或更改Activity
的Orientation
,您的Fragments
就可以了!您不需要用新副本替换旧副本,事实上您不应该!如果您只是删除此行,则不会有问题:
handleFragments(lastFragmentId);
然而如果你这样做是因为你需要你的Fragment
来加载一个新的布局资源 (layout/frag.xml
=> layout-land/frag.xml
) 那么你需要做一些事情像这样:
boolean mResumed = false;
onPause()
mResumed = false;
onResume()
mResumed = true;
...
if(mResumed) handleFragments(lastFragmentId);
【讨论】:
@Greame,正如我在第一个答案中提到的,当活动的onResume
方法完成时,片段还没有恢复。而且您仍然无法执行片段事务。看看这个文件<sdk>\extras\android\compatibility\v4\src\java\android\support\v4\app\FragmentActivity.java
。您可以在onPostResume
或onResumeFragments
方法中执行片段事务,之前调用super
实现。
我不建议在任何其他生命周期状态下调用FragmentTransaction.commit()
,而不是“恢复”(又名运行),我建议仅在指示 Activity 处于正确状态的标志时调用它。跨度>
我现在理解你了。但是,如果您想在每个方向更改时替换片段,那么自己处理方向有什么意义?为什么不让系统处理方向变化,去掉configChanges
参数,只在onCreate
中设置正确的布局和实例化正确的片段?
我同意 - 如果你想使用不同的资源,你应该尽量不要覆盖配置更改。
是的,我摆脱了对配置更改的处理,现在它工作得很好。我只需要确保重要的值在一个全局类中,这样我就可以轻松地重新创建应用程序的状态谢谢 Graeme。【参考方案3】:
您使用的是最新版本的 support-v4 库吗?它解决了我的类似问题。
【讨论】:
【参考方案4】:如果您坚持使用 r7 版本的支持库(例如,因为您正在使用 maven 并且急切地等待更新.. ;)),那么您可以使用 onPostResume
来避免这个问题。如果你的版本是 r11 或以上,那么你可以切换到onResumeFragements
。
【讨论】:
以上是关于Android Fragment 和方向更改导致:IllegalStateException: Can not perform this action after onSaveInstanceStat的主要内容,如果未能解决你的问题,请参考以下文章