“在 onSaveInstanceState 之后无法执行此操作” - 为啥我会从我的活动的 onResume 方法中收到此异常?
Posted
技术标签:
【中文标题】“在 onSaveInstanceState 之后无法执行此操作” - 为啥我会从我的活动的 onResume 方法中收到此异常?【英文标题】:"Can not perform this action after onSaveInstanceState" - why am I getting this exception from my activity's onResume method?“在 onSaveInstanceState 之后无法执行此操作” - 为什么我会从我的活动的 onResume 方法中收到此异常? 【发布时间】:2012-09-09 02:58:50 【问题描述】:我的活动使用 ACTION_IMAGE_CAPTURE 意图调用相机。如果相机活动成功返回,我会在 onActivityResult 回调中设置一个标志,并根据该标志的值在我的 onResume 中启动一个片段,为捕获的图像添加标题。这似乎工作正常。
我刚从“狂野”那里得到一个堆栈跟踪,抱怨我在调用 onSaveInstanceState 后试图提交一个片段事务。但我正在我的 onResume 方法中进行提交!为什么android会抱怨这个?我确实在我的 AndroidManifest.xml 中设置了 android:configChanges="orientation|keyboardHidden|keyboard|screenSize",因此方向更改不应触发此操作....
这发生在运行 4.0.4 的三星 Galaxy S3 (SGH-i747) 上
这是堆栈:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1314)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1325)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:548)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:532)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.performFragmentTransition(AddPhotosActivity2.java:278)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.switchToCaptionsFragment(AddPhotosActivity2.java:438)
at com.Familiar.Android.FamiliarAppV1.AddPhotosActivity2.onResume(AddPhotosActivity2.java:167)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1158)
at android.app.Activity.performResume(Activity.java:4544)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2448)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2486)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1187)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4514)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
at dalvik.system.NativeStart.main(Native Method)
感谢任何帮助或智慧。
【问题讨论】:
查看blog post了解更多信息。 getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"的可能重复 【参考方案1】:我认为我知道答案 - 我正在使用来自 v4 兼容性库的 FragmentActivity,因此我需要在 onResumeFragments 而不是 onResume 中执行我的片段事务。有人可以确认吗?
【讨论】:
在我更改为使用 onResumeFragments 而不是 onResume 来执行我的片段事务之后,这些错误已经消失 - 另一个数据点。我还没有实现空片段解决方法,也不需要。我会注意到我还会定期将我的应用程序更新到最新的 v4 兼容性库,因此谷歌可能会在他们的最后修复一些问题。我的活动不会在配置更改时重新启动 - 我在清单中覆盖它并自己处理配置更改。 我不确定,但这和onPostResume()
一样吗?
@DiscoS2 是的。 onResumeFragments()
保证在活动状态恢复后调用,因此onPostResume()
也将调用(因为onPostResume()
总是在onResumeFragments()
之后调用)。
Google 应该更好地记录这种方式。我仍在等待 Google 在其指南中提及这一点!
我只是在显示插页式广告后遇到了这个问题......旋转后,我的对话框片段从 onResume 正确地开始。【参考方案2】:
你可以使用commitAllowingStateLoss()
的方法
但请注意,您可能会丢失活动状态 正如您在谷歌的android reference 中看到的那样 这通过以下方式解释了两者之间的不同
与 commit() 类似,但允许在保存活动状态后执行提交。这很危险,因为如果活动需要稍后从其状态恢复,提交可能会丢失,因此这应该只用于 UI 状态可以在用户上意外更改的情况。
根据我的经验,它可能会导致 addToBackStack
方法有时不起作用,因此您需要在片段上手动添加它
当然不会保存状态(文本框文本分机)
【讨论】:
【参考方案3】:这对我有用...我自己发现了这个...希望对您有所帮助!
1) 没有全局“静态”FragmentManager / FragmentTransaction。
2) onCreate,总是再次初始化 FragmentManager!
下面的示例:-
public abstract class FragmentController extends AnotherActivity
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
mSavedInstanceState = savedInstanceState;
setDefaultFragments();
protected void setDefaultFragments()
fragmentManager = getSupportFragmentManager();
//check if on orientation change.. do not re-add fragments!
if(mSavedInstanceState == null)
//instantiate the fragment manager
fragmentTransaction = fragmentManager.beginTransaction();
//the navigation fragments
NavigationFragment navFrag = new NavigationFragment();
ToolbarFragment toolFrag = new ToolbarFragment();
fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
fragmentTransaction.commitAllowingStateLoss();
//add own fragment to the nav (abstract method)
setOwnFragment();
【讨论】:
如果您只是使用 getSupportFragmentManager().beginTransaction() 而不是声明片段管理器会怎样?即使那样,错误也会发生。所以这可能不是解决方案。 @kurayami88 是的,这个解决方案对我有用。我仍然有一个全局静态fragmentManager
,但重新初始化 onCreate
中的变量就可以了。【参考方案4】:
更新我想我在这里找到了解释和解决方案:http://code.google.com/p/android/issues/detail?id=23096#c4 我实现了那里发布的 Empty Fragment Workaround 并且到目前为止没有再收到 IllegalStateException。
我像这样在我的活动中添加不可见的状态片段;
@Override
protected void onCreate(final Bundle args)
...
if (args == null)
final FragmentManager fm = this.getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
final Fragment emptyFragmentWithCallback = new EmptyFragmentWithCallbackOnResume();
ft.add(emptyFragmentWithCallback, EmptyFragmentWithCallbackOnResume.TAG);
ft.commit();
以下代码取自以上链接:
public class EmptyFragmentWithCallbackOnResume extends Fragment
OnFragmentAttachedListener mListener = null;
@Override
public void onAttach(SupportActivity activity)
super.onAttach(activity);
try
mListener = (OnFragmentAttachedListener) activity;
catch (ClassCastException e)
throw new ClassCastException(activity.toString() + " must implement OnFragmentAttachedListener");
@Override
public void onResume()
super.onResume();
if (mListener != null)
mListener.OnFragmentAttached();
public interface OnFragmentAttachedListener
public void OnFragmentAttached();
并调用将直观地进入 onResume 或 onResumeFragments 的片段事务到我的自定义 onFragmentAttached 方法中,该方法由不可见状态片段调用。我根本不使用 onResumeFragments,也不会在 onResume 中发出任何片段事务。
所以,总结一下。如果您正在使用支持库和片段,则几乎忘记 onResume,忘记 onResumeFragments 并根据上述解决方法实现您自己的“onResume”。 这有点荒谬。
我无法确认。我有完全一样的问题。即使我在 onResumeFragments 中发出片段事务。 这曾经像我在这里发布的那样工作:IllegalStateException - Fragment support library。
似乎该错误仅发生在 4.0.3 和 4.0.4 上。然而,它既不会总是发生,也不会出现在我的模拟器中。
我正在使用支持库版本。 10 和 API 16。 我在 onResumeFragments 中调用 DialogFragment.show 并不断从一些随机用户那里得到这个荒谬的异常。我无法在本地复制它。
【讨论】:
嗯 - 在我开始使用 onResumeFragments 后,我还没有看到这个问题重现。你打电话给 super.onResumeFragments 对吧? 是的,我调用 onResumeFragments。这个问题显然只是偶尔发生,例如关于方向变化。我可以肯定地说,解决方法为我解决了这个问题。我之前每个小时都会收到错误消息。 嗨 phiebas 我不关注我是否应该从 onResumeFragments 启动一个新片段? 嗨。我的简短回答是否定的,尽管我对这个问题的经验是基于支持 lib rev 10 和 API 16。我不知道该行为最近是否已修复。我使用上述解决方法启动片段,因此我完全忽略 onResumeFragments 并改用自定义的 onFragmentAttached。这解决了我的问题。我已经更新了我的帖子,希望能更清楚【参考方案5】:当我尝试在 onActivityForResult() 方法中显示片段时,我总是得到这个,所以问题是下一个:
-
我的 Activity 已暂停和停止,这意味着 onSaveInstanceState() 已被调用(对于蜂窝前和蜂窝后的设备)。
如果有任何结果我进行了交易以显示/隐藏片段,这会导致此 IllegalStateException。
接下来是我做的:
-
附加值用于确定我想要的操作是否已完成(例如,从相机拍照 - isPhotoTaken) - 它可以是布尔值或整数值,具体取决于您需要多少不同的交易。
在重写的 onResumeFragments() 方法中,我检查了我的值,并在进行了我需要的片段事务之后。在这种情况下,在 onSaveInstanceState 之后没有完成 commit(),因为在 onResumeFragments() 方法中返回了状态。
【讨论】:
【参考方案6】:我首先使用 support v4 库开发面向 android 2.2 (SDK 8) 的应用程序,当我开始使用 4.2 (SDK 17) 时,我的片段也遇到了同样的问题。但是将我的清单更改为 android:minSdkVersion="8" android:targetSdkVersion="17",解决了我的问题。也许这对你也有帮助。
【讨论】:
【参考方案7】:将此添加到您的活动中:
@Override
protected void onSaveInstanceState(Bundle outState)
//No call for super(). Bug on API Level > 11.
【讨论】:
以上是关于“在 onSaveInstanceState 之后无法执行此操作” - 为啥我会从我的活动的 onResume 方法中收到此异常?的主要内容,如果未能解决你的问题,请参考以下文章