Android 使用多个布局会打断方向更改

Android 使用多个布局会打断方向更改,android,android-layout,android-fragments,android-orientation,Android,Android Layout,Android Fragments,Android Orientation,我成功地处理了带有片段的配置更改,但我只为容器使用了一个XML布局 现在我需要使用横向模式的布局,当我打开手机并尝试更改当前显示的片段时,我会收到一个错误: E/AndroidRuntime: FATAL EXCEPTION: main java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.app.FragmentManagerImpl.checkStat

我成功地处理了带有片段的配置更改,但我只为容器使用了一个XML布局

现在我需要使用横向模式的布局,当我打开手机并尝试更改当前显示的片段时,我会收到一个错误:

E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1328)
at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1346)
at android.app.BackStackRecord.commitInternal(BackStackRecord.java:729)
at android.app.BackStackRecord.commit(BackStackRecord.java:705)
以下是我的两个布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" />

问题源于Bluetooth connection message事件调用您的
connected()
方法,然后该方法执行片段事务以显示
ConnectedFragment

这就是正在发生的事情:

  • 用户旋转设备
  • 活动开始关闭,状态已保存
  • 发生连接事件(因为活动尚未完全关闭)
  • 已尝试碎片事务,但状态已保存
你现在有了比赛条件

您需要更仔细地管理这些片段事务。我建议首先在
connected()
的开头添加此代码,然后在事件可能导致碎片事务的任何其他地方添加此代码:

    if (isFinishing()) {
        return;
    }
然后,当活动的状态(可能)已经保存时,您不会尝试片段事务

这一段代码可能无法解决所有问题。您可能需要添加一些生命周期日志记录来分析发生的情况。然后,您可以添加必要的代码,以确保事情按正确的顺序发生

顺便说一句:这个代码

    @Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 0) {
            getFragmentManager().popBackStack();
        } else {
            super.onBackPressed();
        }
    }

是多余的;
FragmentActivity
onBackPressed
方法已经在处理检查片段返回堆栈并在必要时弹出它。也许您打算添加更多自定义后退按钮处理?

问题源于蓝牙连接消息事件调用您的
connected()
方法,然后该方法执行片段事务以显示
ConnectedFragment

这就是正在发生的事情:

  • 用户旋转设备
  • 活动开始关闭,状态已保存
  • 发生连接事件(因为活动尚未完全关闭)
  • 已尝试碎片事务,但状态已保存
你现在有了比赛条件

您需要更仔细地管理这些片段事务。我建议首先在
connected()
的开头添加此代码,然后在事件可能导致碎片事务的任何其他地方添加此代码:

    if (isFinishing()) {
        return;
    }
然后,当活动的状态(可能)已经保存时,您不会尝试片段事务

这一段代码可能无法解决所有问题。您可能需要添加一些生命周期日志记录来分析发生的情况。然后,您可以添加必要的代码,以确保事情按正确的顺序发生

顺便说一句:这个代码

    @Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 0) {
            getFragmentManager().popBackStack();
        } else {
            super.onBackPressed();
        }
    }

是多余的;
FragmentActivity
onBackPressed
方法已经在处理检查片段返回堆栈并在必要时弹出它。也许您打算添加更多自定义后退按钮处理?

因此问题来自我的HandleConnection对象。我没有更新对处理程序的引用,这意味着调用的处理程序引用的是一个已经销毁的活动

我通过在片段事务中使用commitAllowingStateLoss()发现了这一点。而不是只获取
java.lang.IllegalStateException:在onSaveInstanceState之后无法执行此操作

我得到了一个
java.lang.IllegalStateException:活动已被销毁
错误,这使我能够猜测问题所在。

因此问题来自我的HandleConnection对象。我没有更新对处理程序的引用,这意味着调用的处理程序引用的是一个已经销毁的活动

我通过在片段事务中使用commitAllowingStateLoss()发现了这一点。而不是只获取
java.lang.IllegalStateException:在onSaveInstanceState之后无法执行此操作

我得到了一个
java.lang.IllegalStateException:活动已被销毁
错误,允许我猜测问题所在。

发布活动和片段中的代码。问题是其他的。从活动和片段发布代码。问题是别的。谢谢克里斯,我还没有时间尝试你的建议,但我的蓝牙连接只有在用户按下按钮时才会出现。我想这意味着不可能有任何比赛条件(或者我对此一无所知)?在为横向模式添加第二个布局后出现了问题,但在其他方面它以前就工作了(顺便说一句,我仍然会考虑这些建议以避免将来的崩溃!)。出现问题的原因是您无法控制a)蓝牙何时完成连接,b)用户何时旋转设备。这些旋转可能是虚假的——比如说,我把设备放在我的桌子上,这样的移动会导致旋转。这是一个很糟糕的问题,我90%的测试都是针对这些情况,因为我的大多数bug都是在轮换时崩溃的。谢谢克里斯,我还没有时间尝试你的建议,但我的蓝牙连接只有在用户按下按钮时才会发生。我想这意味着不可能有任何比赛条件(或者我对此一无所知)?在为横向模式添加第二个布局后出现了问题,但在其他方面它以前就工作了(顺便说一句,我仍然会考虑这些建议以避免将来的崩溃!)。出现问题的原因是您无法控制a)蓝牙何时完成连接,b)用户何时旋转设备。这些旋转可能是虚假的——比如说,我把设备放在我的桌子上,这样的移动会导致旋转。这是一个很糟糕的问题,我90%的测试都是针对这些条件的,因为大多数
    @Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 0) {
            getFragmentManager().popBackStack();
        } else {
            super.onBackPressed();
        }
    }