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

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。


当前回答

在我的案例中,我发现的最流畅和最简单的解决方案可能是避免在响应活动结果时从堆栈中弹出有问题的片段。所以在onActivityResult()中改变这个调用:

popMyFragmentAndMoveOn();

:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    public void run() {
        popMyFragmentAndMoveOn();
    }
}

对我有帮助。

其他回答

芬兰湾的科特林扩展

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)

关于@Anthonyeef的回答,这里有一个Java的示例代码:

private boolean shouldShowFragmentInOnResume;

private void someMethodThatShowsTheFragment() {

    if (this.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
        showFragment();
    } else {
        shouldShowFragmentInOnResume = true;
    }
}

private void showFragment() {
    //Your code here
}

@Override
protected void onResume() {
    super.onResume();

    if (shouldShowFragmentInOnResume) {
        shouldShowFragmentInOnResume = false;
        showFragment();
    }
}

这里有一个不同的解决方法。

使用私有成员变量,你可以将返回的数据设置为一个意图,然后在super.onResume();

像这样:

private Intent mOnActivityResultIntent = null; 

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}

我也经历过这个问题,每当FragmentActivity的上下文发生变化时(例如屏幕方向被改变等),问题就会发生。所以最好的修复方法就是从FragmentActivity中更新context。

如果状态保存,请使用remove()而不是popup()。

   private void removeFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (fragmentManager.isStateSaved()) {
        List<Fragment> fragments = fragmentManager.getFragments();
        if (fragments != null && !fragments.isEmpty()) {
            fragmentManager.beginTransaction().remove(fragments.get(fragments.size() - 1)).commitAllowingStateLoss();
        }
    }
}