FragmentTransaction的commit和commitAllowingStateLoss的差異

一、什麼是FragmentTransaction?

使用Fragment時。可以經過用戶交互來運行一些動做。比方添加、移除、替換等。java

所有這些改變構成一個集合,這個集合被叫作一個transaction。ide

可以調用FragmentTransaction中的方法來處理這個transaction,並且可以將transaction存進由activity管理的back stack中,這樣用戶就可以進行fragment變化的回退操做。post

可以這樣獲得FragmentTransaction類的實例:this

FragmentManager  mFragmentManager = getSupportFragmentManager();
FragmentTransaction  mFragmentTransaction = mFragmentManager.beginTransaction();

二、commit和executePendingTransactions的差異

用add(), remove(), replace()方法,把所有需要的變化加進去,而後調用commit()方法。將這些變化應用。


在commit()方法以前,你可以調用addToBackStack(),把這個transaction增長back stack中去,這個back stack是由activity管理的。當用戶按返回鍵時,就會回到上一個fragment的狀態。
你僅僅能在activity存儲它的狀態(當用戶要離開activity時)以前調用commit()。假設在存儲狀態以後調用commit()。將會拋出一個異常。線程


這是因爲當activity再次被恢復時commit以後的狀態將丟失。假設丟失也不要緊。那麼使用commitAllowingStateLoss()方法。
code

三、問什麼在存儲狀態以後調用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,看這種方法是怎樣操做的。orm

咱們找到這種方法。
blog

/**
 * @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錯誤。
相關文章
相關標籤/搜索