1

我已成功處理配置更改與片段,但我只使用一個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" /> 

而對於景觀之一:

<?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); 
} 
} 

而對於應顯示的片段:

public class ConnectedFragment extends Fragment { 

private StateModifier stateModifier; 
private IpAddressChangedListener ipAddressChangedListener; 

@Nullable 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    View root = inflater.inflate(R.layout.connected_fragment_layout, container, false); 

    root.findViewById(R.id.remote_control_button).setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      stateModifier.remoteControl(); 
     } 
    }); 

    root.findViewById(R.id.sounds_button).setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      stateModifier.soundsControl(); 
     } 
    }); 

    final EditText editText = (EditText)root.findViewById(R.id.ip_edit_text); 

    root.findViewById(R.id.change_ip_button).setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      ipAddressChangedListener.onIpChangedListener(editText.getText().toString()); 
     } 
    }); 

    return root; 
} 

public void setStateModifier(StateModifier stateModifier) { 
    this.stateModifier = stateModifier; 
} 

public void setIpAddressChangedListener(IpAddressChangedListener ipAddressChangedListener) { 
    this.ipAddressChangedListener = ipAddressChangedListener; 
} 
} 
+0

從活動和片段發佈您的代碼。問題是別的。 –

回答

0

所以問題來自我的HandleConnection對象。我沒有更新對處理程序的引用,這意味着所調用的處理程序是指已經被銷燬的活動。

我發現通過在我的片段事務中使用commitAllowingStateLoss()。而不是隻得到一個java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 我得到java.lang.IllegalStateException: Activity has been destroyed錯誤,這讓我猜猜問題是什麼。

0

問題來源於這樣的事實是,藍牙連接消息事件調用您的connected()方法,然後執行片段事務以顯示ConnectedFragment

因此,這是發生了什麼事:

  • 用戶旋轉設備
  • 活動開始關停,狀態保存
  • 關聯的事件發生(因爲活動尚未完全關閉至今)
  • 嘗試片段交易,但狀態已保存

您現在有一個競賽條件。

您需要更仔細地管理這些碎片交易。我會建議開始在connected()開頭添加該代碼和其他地方的事件可能導致一個片段交易:

if (isFinishing()) { 
     return; 
    } 

那麼活動的狀態發生時(可能),你會不會嘗試片段的交易已經保存。

這個單獨的代碼片段可能無法解決您的所有問題。您可能需要添加一些生命週期日誌記錄來分析什麼時候發生。然後,您可以添加必要的代碼,以確保以正確的順序發生。

順便提及:此代碼

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

是多餘的; FragmentActivityonBackPressed方法已經處理檢查片段返回堆棧並在必要時彈出它。也許你會添加更多的自定義後退按鈕處理?

+0

感謝kris,我還沒有時間去嘗試你的建議,但我的藍牙連接只發生在用戶按下按鈕時。我認爲這意味着不存在任何競爭條件(或者我對此不瞭解)?在爲橫向模式添加第二個佈局後,問題出現了,但是之前它的工作原理(我仍然會將這些建議考慮在內以避免將來的崩潰!)。 – pLesur

+0

問題出現是因爲您無法控制a)BlueTooth何時完成連接,b)用戶何時旋轉設備。這些旋轉可能會發生虛假 - 我將設備放在我的桌子上,並且運動導致旋轉。這是一個邪惡的問題,可能90%的測試只是針對這些情況,因爲我的大多數錯誤都是輪換崩潰。 –