源碼分析Fragment的BackStack管理過(guò)程

1. Fragment基本用法

為了管理Activity中的fragments,需要調(diào)用Activity中的getFragmentManager()方法。因?yàn)镕ragmentManager的API是在Android 3.0,也即API level 11開(kāi)始引入的,所以對(duì)于之前的版本,需要使用support library v4中的FragmentActivity,并且使用getSupportFragmentManager()方法。

用FragmentManager可以做的工作有:

得到Activity中存在的fragment:

使用findFragmentById()或findFragmentByTag()方法。

將fragment彈出back stack:

popBackStack():

將back stack中最后一次的fragment轉(zhuǎn)換彈出。如果沒(méi)有可以出棧的東西,返回false。

這個(gè)函數(shù)是異步的:它將彈出棧的請(qǐng)求加入隊(duì)列,但是這個(gè)動(dòng)作直到應(yīng)用回到事件循環(huán)才會(huì)執(zhí)行。

為back stack加上監(jiān)聽(tīng)器:

addOnBackStackChangedListener()

使用Fragment時(shí),可以執(zhí)行一些動(dòng)作,比如增加、移除、替換等。所有這些改變構(gòu)成一個(gè)集合,這個(gè)集合被叫做一個(gè)transaction。

可以調(diào)用FragmentTransaction中的方法來(lái)處理這個(gè)transaction.

以這樣得到FragmentTransaction類(lèi)的實(shí)例:

每個(gè)transaction是一組同時(shí)執(zhí)行的變化的集合。用add(), remove(), replace()方法,把所有需要的變化加進(jìn)去,然后調(diào)用commit()方法,將這些變化應(yīng)用。在commit()方法之前,你可以調(diào)用addToBackStack(),把這個(gè)transaction加入back stack中去,這個(gè)back stack是由activity管理的,當(dāng)用戶按返回鍵時(shí),就會(huì)回到上一個(gè)fragment的狀態(tài)。下面的代碼非常典型,用一個(gè)新的fragment取代之前的fragment,并且將之前的狀態(tài)存儲(chǔ)在back stack中。

通過(guò)調(diào)用addToBackStack(),commit()的一系列轉(zhuǎn)換作為一個(gè)transaction被存儲(chǔ)在back stack中,用戶按Back鍵可以返回上一個(gè)轉(zhuǎn)換前的狀態(tài)。

調(diào)用commit()方法并不能立即執(zhí)行transaction中包含的改變動(dòng)作,commit()方法把transaction加入activity的UI線程隊(duì)列中。

下面我們對(duì)上述代碼中出現(xiàn)的函數(shù)進(jìn)行分析,以此來(lái)逐步學(xué)習(xí)Fragment的管理機(jī)制。

getSupportFragmentManager():

該函數(shù)返回類(lèi)型是FragmentManager,F(xiàn)ragmentManager是一個(gè)抽象類(lèi),其實(shí)現(xiàn)類(lèi)是FragmentManager.FragmentManagerImpl

beginTransaction():

該函數(shù)在FragmentManagerIMpl中的源碼如下:

返回一個(gè)BackStackRecord對(duì)象,該對(duì)象是FragmentTranscation的一個(gè)子類(lèi)。

BackStackRecord的聲明如下:

該類(lèi)實(shí)現(xiàn)了一個(gè)重要的接口:FragmentManager.BackStackEntry, 該接口代表了fragment back stack的一個(gè)入口。可以用FragmentManager.getBackStackEntry()來(lái)檢索BackStackEntry。

接下來(lái)執(zhí)行transaction.replace(), 查看BackStackRecord,調(diào)用過(guò)程源碼如下:

我們發(fā)現(xiàn),replace()最終調(diào)用的函數(shù)為doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd), 將Fragment和對(duì)Fragment所進(jìn)行的操作放到op鏈表中:

該函數(shù)首先設(shè)置fragment的mFragmentManager屬性,然后再設(shè)置其mContainerId和mFragmentId,最后創(chuàng)建Op對(duì)象,然設(shè)置相應(yīng)自段,其中cmd自動(dòng)用來(lái)標(biāo)識(shí)事務(wù)的類(lèi)型,分為如下幾類(lèi):

static final int OP_NULL = 0;

static final int OP_ADD = 1;

static final int OP_REPLACE = 2;

static final int OP_REMOVE = 3;

static final int OP_HIDE = 4;

static final int OP_SHOW = 5;

static final int OP_DETACH = 6;

static final int OP_ATTACH = 7;

每個(gè)字段的意思可直接通過(guò)英文名稱(chēng)獲知。Op()類(lèi)是BackStackRecord中聲明的結(jié)構(gòu)體,本質(zhì)上是一個(gè)雙向鏈表的Node。addOp()如下:

該函數(shù)將Op對(duì)象添加到鏈表的末尾,并將mNumOp的值增一。

transaction.addToBackStack(null)設(shè)置了mAddToBackStack為true,源碼如下:

此函數(shù)將mAddToBackStack自段設(shè)置為true,并設(shè)置mName字段。

最后調(diào)用transaction.commit()來(lái)執(zhí)行transaction。commit()的調(diào)用過(guò)程代碼如下:

由于mAddToBackStack為true,所以會(huì)用FragmentManager為BackstackRecorder也即FragmentTransaction分配一個(gè)index,分配過(guò)程如下:

FragmentManager用mAvailBackStackIndices和mBackStackIndices兩個(gè)數(shù)組來(lái)為BackStackRecord分配Index。mAvailBackStackIndices用來(lái)存儲(chǔ)在mBackStackIndices中能夠分配的Index,mBackStackIndices則用來(lái)保存BackStackRecord。這利用兩個(gè)數(shù)組可以減少對(duì)mBackStackIndices的動(dòng)態(tài)分配大小的次數(shù),是一個(gè)以空間換時(shí)間的策略。上面的代碼首先判斷是否有可用的Index分配給BackStackRecord,若無(wú)則直接將BackStackRecord插入到mBackStackIndices;若存在的話則從mAvailBackStackIndices的隊(duì)尾取出一個(gè)index,然后設(shè)置mBackStackIndices中該index下的值。

讓我們回到commit()中,該函數(shù)最后執(zhí)行mManager.enqueAction(),源碼如下:

該函數(shù)首先進(jìn)行狀態(tài)監(jiān)測(cè),查看該Fagment所在的Activity的生命周期是否處于Saving Activity之前,因?yàn)锳ctivity保存狀態(tài)往往是由用戶離開(kāi)那個(gè)Activity所造成的,在此之后執(zhí)行commit會(huì)丟失一些狀態(tài)信息。針對(duì)這種情況,可以使用commitAllowingStateLoss().最后將BackStackRecord加入到執(zhí)行隊(duì)列中。當(dāng)?shù)谝淮瓮鶊?zhí)行

隊(duì)列中添加消息時(shí),首先會(huì)從消息隊(duì)列中所有callback屬性為mExecCommit的消息刪除,然后重新將mExecCommit添加到消息隊(duì)列。mExecCommit的定義如下:

execPendingActions()只能在主線程內(nèi)被調(diào)用,其內(nèi)部通過(guò)一個(gè)循環(huán)對(duì)mPendingActions中的Actions進(jìn)行執(zhí)行。值得注意的是,每執(zhí)行一次循環(huán),mPendingActions中的所有Action都會(huì)被添加到一個(gè)臨時(shí)數(shù)組中,然后這個(gè)數(shù)組被變量一遍以執(zhí)行數(shù)組中的每個(gè)Runnable。同時(shí),每個(gè)Runnable直接被調(diào)用了run,而不是開(kāi)個(gè)線程執(zhí)行的。當(dāng)這個(gè)Runnable在執(zhí)行的時(shí)候,mPendingActions數(shù)組可能會(huì)被添加內(nèi)容。當(dāng)某一時(shí)刻mPendingActions中的內(nèi)容為空,則while循環(huán)退出。此部分代碼如下:

由于BackstackRecorder實(shí)現(xiàn)了Runnable,我們來(lái)看看BackStackRecorder中的run(),如下所示:



addBackStackState()的源碼如下:


可以看到傳說(shuō)中的BackStack就是在這里被創(chuàng)建的, FragmentManager中的BackStack主要是用來(lái)存儲(chǔ)FragmentTransaction的。

小結(jié):

FragmentTransaction中的Op鏈用來(lái)保存add、remove、replace等action,在FragmentTransaction的run執(zhí)行時(shí),Op鏈會(huì)被變量以調(diào)整每個(gè)節(jié)點(diǎn)的內(nèi)容。

FragmentManager使用一個(gè)BackStack來(lái)管理FragmentTransaction;使用mAdded數(shù)組來(lái)添加被add的Fragment,F(xiàn)ragment的創(chuàng)建、顯示等行為都受FragmentManager的控制。

FragmentManager中的moveToState()是一個(gè)非常重要的函數(shù),在FragmentTransaction run的時(shí)候被調(diào)用。下次我們將深入這個(gè)函數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容