標籤:android開發 fragment fragmenttransaction commit commitallowingstatel
1、什麼是FragmentTransaction?
使用Fragment時,可以通過使用者互動來執行一些動作,比如增加、移除、替換等。
所有這些改變構成一個集合,這個集合被叫做一個transaction。
可以調用FragmentTransaction中的方法來處理這個transaction,並且可以將transaction存進由activity管理的back stack中,這樣使用者就可以進行fragment變化的後援動作。
可以這樣得到FragmentTransaction類的執行個體:
FragmentManager mFragmentManager = getSupportFragmentManager();FragmentTransaction mFragmentTransaction = mFragmentManager.beginTransaction();
2、commit和executePendingTransactions的區別用add(), remove(), replace()方法,把所有需要的變化加進去,然後調用commit()方法,將這些變化應用。
在commit()方法之前,你可以調用addToBackStack(),把這個transaction加入back stack中去,這個back stack是由activity管理的,當使用者按返回鍵時,就會回到上一個fragment的狀態。
你只能在activity儲存它的狀態(當使用者要離開activity時)之前調用commit(),如果在儲存狀態之後調用commit(),將會拋出一個異常。
這是因為當activity再次被恢複時commit之後的狀態將丟失。如果丟失也沒關係,那麼使用commitAllowingStateLoss()方法。
3、問什麼在儲存狀態之後調用commit會報異常?我們查看Android源碼發現FragmentManager和FragmentTransaction是一個虛類
那他們在activity中的執行個體化代碼是如何處理的呢?
首先是getSupportFragmentManager的方法
/** * Return the FragmentManager for interacting with fragments associated * with this activity. */ public FragmentManager getSupportFragmentManager() { return mFragments; }
尋找到mFragments。
final FragmentManagerImpl mFragments = new FragmentManagerImpl();
我們發現FragmentManagerImpl是繼承於FragmentManager的一個實體類
/** * Container for fragments associated with an activity. */final class FragmentManagerImpl extends FragmentManager { ........ @Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); } ........ }
為了簡便我們刪除了一些不要的代碼只留下關鍵的方法。
通過這段代碼,我們可以查看到beginTransaction方法實際返回的是一個繼承於FragmentTransaction的BackStackRecord類
我們來查看BackStackRecord的代碼,查看他的用法
/** * @hide Entry of an operation on the fragment back stack. */final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable {..........public int commit() { return commitInternal(false); } public int commitAllowingStateLoss() { return commitInternal(true); }int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this); mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; }..........}
繞了大半天,終於找到commit方法和commitAllowingStateLoss方法,他們都同時調用了commitInternal方法,只是傳的參數略有不同,一個是true,一個是false。我們發現在執行這個方法之前會首先對mCommitted進行判斷,根據代碼語義我們可以知道mCommitted就是是否已經commit的意思
最後,commitInternal調用了mManager.enqueueAction的方法。讓我們回到FragmentManager,看這個方法是如何操作的。我們找到這個方法。
/** * @hide Entry of an operation on the fragment back stack. */final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable {..........public int commit() { return commitInternal(false); } public int commitAllowingStateLoss() { return commitInternal(true); }int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this); mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; }..........}
經分析後,我們可以發現,此方法在對 commit和commitAllowingStateLoss的傳參進行判斷後,將任務扔進activity的線程隊列中。那這個兩個方法區別就在傳參判斷後的處理方法checkStateLoss,那接下來,讓我們查看一下checkStateLoss方法,看對參數進行判斷後,做了什麼樣的處理。
private void checkStateLoss() { if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } }
ok,到這裡,真相總算大明,當使用commit方法時,系統將進行狀態判斷,如果狀態(mStateSaved)已經儲存,將發生"Can not perform this action after onSaveInstanceState"錯誤。
如果mNoTransactionsBecause已經存在,將發生"Can not perform this action inside of " + mNoTransactionsBecause錯誤。