正在获取异常“ IllegalStateException:onSaveInstanceState之后无法执行此操作”

依然范特西╮ 提交于 2020-03-01 12:54:20

我有一个Live Android应用程序,从市场上我收到了以下堆栈跟踪信息,我不知道为什么它发生在应用程序代码中而不是发生,而是由应用程序中的某些或其他事件引起的(假设)

我没有使用Fragments,但仍然有FragmentManager的引用。 如果有人可以对某些隐藏的事实有所了解,以避免此类问题:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)  

#1楼

这是到目前为止我遇到的最愚蠢的错误。 我有一个适用于API <11Fragment应用程序,并且在API> 11Force Closing

我真的不知道在调用saveInstance它们在Activity生命周期内发生了什么变化,但是我在这里解决了这个问题:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

我只是不打电话给.super()而一切都很好。 我希望这可以节省您一些时间。

编辑:经过更多研究,这是支持包中的已知错误

如果您需要保存实例,并向outState Bundle添加内容,则可以使用以下命令:

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

EDIT2:如果您在后台进行Activity后尝试执行事务,也可能会发生这种情况。 为了避免这种情况,您应该使用commitAllowingStateLoss()

EDIT3:从我记得的情况来看 ,上述解决方案正在解决早期support.v4库中的问题。 但是,如果您仍然对此有疑问,还必须阅读@AlexLockwood的博客: 碎片事务和活动状态丢失

博客文章的摘要(但我强烈建议您阅读):

  • 永远不要在蜂巢前onPause()和蜂巢后onStop()之后commit()交易
  • Activity生命周期方法内提交事务时要小心。 使用 onCreate()onResumeFragments()onPostResume()
  • 避免在异步回调方法中执行事务
  • 只能将commitAllowingStateLoss()用作最后的手段

#2楼

我通过onconfigurationchanged解决了这个问题。 诀窍是根据android活动的生命周期,当您显式调用intent(相机intent或任何其他一个)时; 活动被暂停,在这种情况下将调用onsavedInstance。 将设备旋转到活动进行期间以外的其他位置时; 进行片段提交等片段操作会导致非法状态异常。 对此有很多抱怨。 这与android活动生命周期管理和正确的方法调用有关。 为了解决这个问题,我做到了:1-重写活动的onsavedInstance方法,并确定当前的屏幕方向(人像或风景),然后在暂停活动之前为其设置屏幕方向。 这样,如果您的活动被另一个旋转,则您可以锁定该屏幕的旋转。 2-然后,覆盖活动的onresume方法,现在将方向模式设置为传感器,以便在调用onsaved方法后,它将再调用一次onconfiguration来正确处理旋转。

您可以将此代码复制/粘贴到您的活动中以进行处理:

@Override
protected void onSaveInstanceState(Bundle outState) {       
    super.onSaveInstanceState(outState);

    Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
    int orientation =this.getDisplayOrientation();
    //Lock the screen orientation to the current display orientation : Landscape or Potrait
    this.setRequestedOrientation(orientation);
}

//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device 
public int getDisplayOrientation() {
    Display getOrient = getWindowManager().getDefaultDisplay();

    int orientation = getOrient.getOrientation();

    // Sometimes you may get undefined orientation Value is 0
    // simple logic solves the problem compare the screen
    // X,Y Co-ordinates and determine the Orientation in such cases
    if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        Configuration config = getResources().getConfiguration();
        orientation = config.orientation;

        if (orientation == Configuration.ORIENTATION_UNDEFINED) {
        // if height and widht of screen are equal then
        // it is square orientation
            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else { //if widht is less than height than it is portrait
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else { // if it is not any of the above it will defineitly be landscape
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        }
    }
    return orientation; // return value 1 is portrait and 2 is Landscape Mode
}

@Override
public void onResume() {
    super.onResume();
    Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
} 

#3楼

查看导致此问题的原因的Android源代码会发现FragmentManagerImpl类(活动中可用的实例)中的标志mStateSaved的值为true。 当从Activity#onSaveInstanceState调用保存后退堆栈(saveAllState)时,将其设置为true。 之后,从ActivityThread进行的调用不会使用FragmentManagerImpl#noteStateNotSaved()dispatch()可用的重置方法来重置此标志。

我的看法是,有一些可用的修复程序,具体取决于您的应用程序在做什么和使用:

好方法

在此之前:我会为Alex Lockwood的文章做广告。 然后,从我到目前为止所做的事情来看:

  1. 对于不需要保留任何状态信息的片段和活动,请调用commitAllowStateLoss 。 来自文档:

    允许在保存活动状态后执行提交。 这很危险,因为如果以后需要将活动从其状态还原,则提交可能会丢失,因此仅应在UI状态可以在用户上意外更改的情况下使用。 我想如果片段显示只读信息就可以使用。 或者即使它们确实显示了可编辑的信息,也可以使用回调方法保留已编辑的信息。

  2. 在事务提交之后(您刚刚调用了commit() ),立即调用FragmentManager.executePendingTransactions()

不推荐的方法:

  1. 如上述Ovidiu Latcu所述,请勿调用super.onSaveInstanceState() 。 但这意味着您将丢失活动的整个状态以及碎片状态。

  2. 覆盖onBackPressed ,在那里仅调用finish() 。 如果您的应用程序不使用Fragments API,则应该没问题; 与super.onBackPressed一样,存在对FragmentManager#popBackStackImmediate()的调用。

  3. 如果您同时使用Fragments API,并且活动状态很重要/很重要,则可以尝试使用反射API FragmentManagerImpl#noteStateNotSaved()进行调用。 但这是黑客,或者可以说这是一种解决方法。 我不喜欢它,但是就我而言,这是可以接受的,因为我有一个来自旧版应用程序的代码,该代码使用了不赞成使用的代码( TabActivity和隐式LocalActivityManager )。

以下是使用反射的代码:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    invokeFragmentManagerNoteStateNotSaved();
}

@SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
    /**
     * For post-Honeycomb devices
     */
    if (Build.VERSION.SDK_INT < 11) {
        return;
    }
    try {
        Class cls = getClass();
        do {
            cls = cls.getSuperclass();
        } while (!"Activity".equals(cls.getSimpleName()));
        Field fragmentMgrField = cls.getDeclaredField("mFragments");
        fragmentMgrField.setAccessible(true);

        Object fragmentMgr = fragmentMgrField.get(this);
        cls = fragmentMgr.getClass();

        Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
        noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
        Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
    } catch (Exception ex) {
        Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
    }
}

干杯!


#4楼

阅读http://chris-alexander.co.uk/on-engineering/dev/android-fragments-within-fragments/

文章。 fragment.isResumed()检查可以帮助我使用onSaveInstanceState方法来实现onDestroyView。


#5楼

这对我有用...我自己发现了...希望对您有帮助!

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();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!