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

时间:2016-03-21 16:49:28

标签: android android-layout android-fragments android-orientation

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

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

public SocketChannel getChannel() {
    return null;
}

以下是我的两个布局:

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" />

正如你所看到的,除了方向外,两者完全相同。 我想问题可能来自重用使用其他方向添加的旧片段?

感谢您的帮助

编辑: 我的活动代码

<?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="horizontal" />

对于应该显示的片段:

package crysteo.pluggicontroller;

public class MainFragmentActivity extends FragmentActivity implements StateModifier,IpAddressChangedListener {

private RemoteFragment remoteFragment;
private MainFragment mainFragment;
private DeviceListFragment deviceListFragment;
private RetainedFragment retainedFragment;
private HandleConnection handleConnection;
private ConnectedFragment connectedFragment;
private CameraFragment cameraFragment;
private final PluggiHandler handler = new PluggiHandler(this);


private static class PluggiHandler extends Handler {
    private final WeakReference<MainFragmentActivity> mainFragmentActivityWeakReference;

    public PluggiHandler(MainFragmentActivity mainFragmentActivity) {
        this.mainFragmentActivityWeakReference = new WeakReference<>(mainFragmentActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        MainFragmentActivity mainFragmentActivity = this.mainFragmentActivityWeakReference.get();
        BluetoothConstants.BluetoothMessageWhat msgEnum = BluetoothConstants.BluetoothMessageWhat.values()[msg.what];
        switch (msgEnum) {
            case MESSAGE_STATE_CHANGE:
                BluetoothConstants.BluetoothStates argEnum = BluetoothConstants.BluetoothStates.values()[msg.arg1];
                switch (argEnum) {
                    case STATE_CONNECTED:
                        mainFragmentActivity.connected();
                        break;
                    case STATE_CONNECTING:
                        Toast.makeText(mainFragmentActivity.getApplicationContext(), R.string.connecting_bluetooth, Toast.LENGTH_SHORT).show();
                        break;
                    case STATE_NONE:
                        break;
                    default:
                        break;
                }
                break;
            case MESSAGE_WRITE:
                /*byte[] writeBuf = (byte[]) msg.obj;
                // construct a string from the buffer
                String writeMessage = new String(writeBuf);
                mConversationArrayAdapter.add("Me:  " + writeMessage);*/
                break;
            case MESSAGE_READ:
                /*byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readMessage = new String(readBuf, 0, msg.arg1);
                mConversationArrayAdapter.add(mConnectedDeviceName + ":  " + readMessage);*/
                break;
            case MESSAGE_DEVICE_NAME:
                Toast.makeText(mainFragmentActivity, String.format(mainFragmentActivity.getResources().getString(R.string.connected_bluetooth),
                        msg.getData().getString(Constants.DEVICE_NAME)), Toast.LENGTH_SHORT).show();
                break;
            case MESSAGE_TOAST:
                if (null != mainFragmentActivity) {
                    Toast.makeText(mainFragmentActivity, msg.getData().getString(Constants.TOAST),
                            Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
        mainFragmentActivity.connected();
    }
}

;


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);

    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();

    if (savedInstanceState == null) {
        retainedFragment = new RetainedFragment();
        fragmentTransaction.add(retainedFragment, RetainedFragment.class.toString());
        handleConnection = new HandleConnection(this, handler);
        retainedFragment.setHandleConnection(handleConnection);

        deviceListFragment = new DeviceListFragment();
        remoteFragment = new RemoteFragment();
        mainFragment = new MainFragment();
        connectedFragment = new ConnectedFragment();
        cameraFragment = new CameraFragment();
        fragmentTransaction.add(R.id.main_layout_container, mainFragment, MainFragment.class.toString());
    } else {
        mainFragment = (MainFragment) getFragmentManager().findFragmentByTag(MainFragment.class.toString());
        deviceListFragment = (DeviceListFragment) getFragmentManager().findFragmentByTag(DeviceListFragment.class.toString());
        remoteFragment = (RemoteFragment) getFragmentManager().findFragmentByTag(RemoteFragment.class.toString());
        retainedFragment = (RetainedFragment) getFragmentManager().findFragmentByTag(RetainedFragment.class.toString());
        connectedFragment = (ConnectedFragment) getFragmentManager().findFragmentByTag(ConnectedFragment.class.toString());
        cameraFragment = (CameraFragment) getFragmentManager().findFragmentByTag(CameraFragment.class.toString());

        if (mainFragment == null)
            mainFragment = new MainFragment();

        if (deviceListFragment == null)
            deviceListFragment = new DeviceListFragment();

        if (remoteFragment == null)
            remoteFragment = new RemoteFragment();

        if (connectedFragment == null)
            connectedFragment = new ConnectedFragment();

        if (cameraFragment == null)
            cameraFragment = new CameraFragment();

        handleConnection = retainedFragment.getHandleConnection();
    }

    fragmentTransaction.commit();

    mainFragment.setBluetoothListener(handleConnection);
    if (retainedFragment.getSelectedMac() != null)
        mainFragment.onMacAddressChanged(retainedFragment.getSelectedMac());
    remoteFragment.setHandleConnection(handleConnection);
    mainFragment.setStateModifier(this);
    deviceListFragment.setStateModifier(this);
    deviceListFragment.setMacAddressChangedListener(mainFragment);
    connectedFragment.setStateModifier(this);
    connectedFragment.setIpAddressChangedListener(this);
}

@Override
public void onDestroy() {
    retainedFragment.setHandleConnection(handleConnection);
    super.onDestroy();
}

@Override
public void listDevices() {
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.main_layout_container, deviceListFragment, DeviceListFragment.class.toString());
    fragmentTransaction.addToBackStack("listDevices");
    fragmentTransaction.commit();
}

@Override
public void deviceSelected(String macAddress) {
    getFragmentManager().popBackStack();
    retainedFragment.setSelectedMac(macAddress);
    mainFragment.onMacAddressChanged(macAddress);
}

@Override
public void connected() {
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.main_layout_container, connectedFragment, ConnectedFragment.class.toString());
    fragmentTransaction.addToBackStack("connected");
    fragmentTransaction.commit();
}

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

@Override
public void remoteControl() {
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.remove(connectedFragment);
    fragmentTransaction.add(R.id.main_layout_container, cameraFragment, CameraFragment.class.toString());
    fragmentTransaction.add(R.id.main_layout_container, remoteFragment, RemoteFragment.class.toString());
    fragmentTransaction.addToBackStack("manualMode");
    fragmentTransaction.commit();
}

@Override
public void soundsControl() {
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.main_layout_container, cameraFragment, CameraFragment.class.toString());
    fragmentTransaction.addToBackStack("soundControl");
    fragmentTransaction.commit();
}

@Override
public void infoDisplay() {

}

@Override
public void onIpChangedListener(String ip) {
    cameraFragment.setIp(ip);
}
}

2 个答案:

答案 0 :(得分:0)

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

所以这就是发生的事情:

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

你现在有竞争条件。

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

    if (isFinishing()) {
        return;
    }

然后,当活动的状态(可能)已经保存时,您不会尝试进行片段交易。

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

顺便说一句:此代码

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

是多余的; FragmentActivity的{​​{1}}方法已经处理了检查片段后备栈并在必要时弹出它。也许你会增加一些自定义后退按钮处理?

答案 1 :(得分:0)

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

我发现在我的片段事务中使用commitAllowingStateLoss()。我没有得到java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState ,而是出现了java.lang.IllegalStateException: Activity has been destroyed错误,这让我猜到了问题所在。