我在市场上从我的应用程序获得用户报告,交付以下异常:

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.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
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.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
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:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
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:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

显然这与FragmentManager有关,而我并不使用它。堆栈跟踪没有显示任何我自己的类,所以我不知道这个异常发生在哪里以及如何防止它。

为了记录:我有一个tabhost,在每个选项卡中都有一个在活动之间切换的ActivityGroup。


当前回答

如果你有崩溃与popBackStack()或popBackStackImmediate()方法,请尝试修复:

        if (!fragmentManager.isStateSaved()) {
            fragmentManager.popBackStackImmediate();
        }

这对我来说也很管用。

其他回答

我找到了一个解决这类问题的下流方法。如果您出于某种原因(我有时间限制的原因)仍然希望保留ActivityGroups,您只需实现即可

public void onBackPressed() {}

在Activity中做一些回显代码。即使旧设备上没有这样的方法,这个方法也会被新设备调用。

我最终创建了一个基本片段,并使我的应用程序中的所有片段扩展它

public class BaseFragment extends Fragment {

    private boolean mStateSaved;

    @CallSuper
    @Override
    public void onSaveInstanceState(Bundle outState) {
        mStateSaved = true;
        super.onSaveInstanceState(outState);
    }

    /**
     * Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
     * would otherwise occur.
     */
    public void showAllowingStateLoss(FragmentManager manager, String tag) {
        // API 26 added this convenient method
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (manager.isStateSaved()) {
                return;
            }
        }

        if (mStateSaved) {
            return;
        }

        show(manager, tag);
    }
}

然后,当我试图显示一个片段时,我使用showAllowingStateLoss而不是show

是这样的:

MyFragment.newInstance()
.showAllowingStateLoss(getFragmentManager(), MY_FRAGMENT.TAG);

我从这个PR上找到了这个解决方案:https://github.com/googlesamples/easypermissions/pull/170/files

请在这里检查我的答案。基本上我只需要:

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

不要在saveInstanceState方法上调用super()。这把事情搞砸了……

这是支持包中的一个已知错误。

如果你需要保存实例并添加一些东西到outState Bundle中,你可以使用以下方法:

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

最后,正确的解决方案是(如评论所示)使用:

transaction.commitAllowingStateLoss();

当添加或执行导致异常的FragmentTransaction时。

将getFragmentManager()更改为getChildFragmentManager()。不要使用父FragmentManager,尽量使用self。

芬兰湾的科特林扩展

fun FragmentManager?.replaceAndAddToBackStack(
    @IdRes containerViewId: Int,
    fragment: () -> Fragment,
    tag: String
) {
    // Find and synchronously remove a fragment with the same tag.
    // The second transaction must start after the first has finished.
    this?.findFragmentByTag(tag)?.let {
        beginTransaction().remove(it).commitNow()
    }
    // Add a fragment.
    this?.beginTransaction()?.run {
        replace(containerViewId, fragment, tag)
        // The next line will add the fragment to a back stack.
        // Remove if not needed.
        // You can use null instead of tag, but tag is needed for popBackStack(), 
        // see https://stackoverflow.com/a/59158254/2914140
        addToBackStack(tag)
    }?.commitAllowingStateLoss()
}

用法:

val fragment = { SomeFragment.newInstance(data) }
fragmentManager?.replaceAndAddToBackStack(R.id.container, fragment, SomeFragment.TAG)