java.lang.IllegalStateException: Fragment already added異常的處理
1.當快速雙擊呼叫FragmentTransaction.add()方法新增fragmentA,而fragmentA不是每次單獨生成的因為DialogFragment.show()內部呼叫了FragmentTransaction.add()方法,所以呼叫DialogFragment.show()方法時候也可能會出現這個異常。
開始想在add()方法時候,先判斷fragmentA.isAdded(),如下呼叫可以避免該異常:
if(!fragmentA.isAdded()){ FragmentManager manager = ((FragmentActivity)context).getSupportFragmentManager(); FragmentTransaction ft = manager.beginTransaction(); ft.add(fragmentA, "fragment_name"); ft.commit(); }
後來發現,在不斷快速切換不同的Fragment的時候,isAdded()偶爾會顯示false,就因為isAdded()顯示了false,那麼 ft.add( R.id.main_content, fragment )就會再次執行一次,就會報錯,說明通過isAdded()這個方法判斷Fragment是否被add可能並不準確。
最終解決方法:
解決方法就是每次add的時候加上一個tag,然後不僅要通過isAdded()判斷Fragment是否add,還要通過FragmentManager.findFragmentByTag(tag)獲取Fragment,然後判斷此Fragment是否為null。
private void addFragment(FragmentManager fm, Fragment fragment, String tag) { if (!fragment.isAdded()&&null == fm.findFragmentByTag( tag )) { FragmentTransaction ft = fm.beginTransaction(); fm.executePendingTransactions(); ft.add( R.id.main_content, fragment, tag ); ft.commitAllowingStateLoss(); } }
2.DialogFragment的show方法引發的該bug
檢視DialogFragment的show方法的原始碼,發現每次show的時候都會提交一個add fragment的事務:
public void show(FragmentManager manager, String tag) { mDismissed = false; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commit(); }
所以當快速多次點選按鈕時,呼叫了多次的show方法,添加了多個add事務(add事務A、add事務B等等)。
然後系統在執行事務佇列時,在執行了add Fragment後,發現又要add這個fragment,就報異常了,FragmentManager中原始碼:
public void addFragment(Fragment fragment, boolean moveToStateNow) { if (DEBUG) Log.v(TAG, "add: " + fragment); makeActive(fragment); if (!fragment.mDetached) { if (mAdded.contains(fragment)) { throw new IllegalStateException("Fragment already added: " + fragment); } synchronized (mAdded) { mAdded.add(fragment); } fragment.mAdded = true; fragment.mRemoving = false; if (fragment.mView == null) { fragment.mHiddenChanged = false; } if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } if (moveToStateNow) { moveToState(fragment); } } }
就是快速多次點選按鈕時,添加了多個連續的add事務,
而系統在執行add fragment時如果已經add過了當前fragment,則不允許再add,add就報異常。
解決辦法:
既然知道異常的原因是【執行了多次show方法,添加了多個連續的add事務】
那我們就改寫下show方法,讓add事務不連續,每次add之前都把原來的remove掉。
解決程式碼如下:
public class TestDialogFragment extends DialogFragment { 其他程式碼...... @Override public void show(FragmentManager manager, String tag) { try { //在每個add事務前增加一個remove事務,防止連續的add manager.beginTransaction().remove(this).commit(); super.show(manager, tag); } catch (Exception e) { //同一例項使用不同的tag會異常,這裡捕獲一下 e.printStackTrace(); } }