“在 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 方法中收到此异常?的主要内容,如果未能解决你的问题,请参考以下文章

从广播接收器更改片段