1. Fragment基本用法
為了管理Activity中的fragments,需要調(diào)用Activity中的getFragmentManager()方法们豌。因?yàn)镕ragmentManager的API是在Android 3.0,也即API level 11開始引入的浅妆,所以對于之前的版本,需要使用support library v4中的FragmentActivity,并且使用getSupportFragmentManager()方法意荤。
用FragmentManager可以做的工作有:
得到Activity中存在的fragment:
使用findFragmentById()或findFragmentByTag()方法烟馅。
將fragment彈出back stack:
popBackStack():
將back stack中最后一次的fragment轉(zhuǎn)換彈出。如果沒有可以出棧的東西康辑,返回false摄欲。
這個(gè)函數(shù)是異步的:它將彈出棧的請求加入隊(duì)列,但是這個(gè)動(dòng)作直到應(yīng)用回到事件循環(huán)才會(huì)執(zhí)行疮薇。
為back stack加上監(jiān)聽器:
addOnBackStackChangedListener()
使用Fragment時(shí)胸墙,可以執(zhí)行一些動(dòng)作,比如增加按咒、移除迟隅、替換等。所有這些改變構(gòu)成一個(gè)集合励七,這個(gè)集合被叫做一個(gè)transaction智袭。
可以調(diào)用FragmentTransaction中的方法來處理這個(gè)transaction.
以這樣得到FragmentTransaction類的實(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中。
通過調(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ì)列中乍狐。
下面我們對上述代碼中出現(xiàn)的函數(shù)進(jìn)行分析,以此來逐步學(xué)習(xí)Fragment的管理機(jī)制固逗。
getSupportFragmentManager():
該函數(shù)返回類型是FragmentManager浅蚪,F(xiàn)ragmentManager是一個(gè)抽象類,其實(shí)現(xiàn)類是FragmentManager.FragmentManagerImpl
beginTransaction():
該函數(shù)在FragmentManagerIMpl中的源碼如下:
返回一個(gè)BackStackRecord對象烫罩,該對象是FragmentTranscation的一個(gè)子類惜傲。
BackStackRecord的聲明如下:
該類實(shí)現(xiàn)了一個(gè)重要的接口:FragmentManager.BackStackEntry, 該接口代表了fragment back stack的一個(gè)入口”丛埽可以用FragmentManager.getBackStackEntry()來檢索BackStackEntry盗誊。
接下來執(zhí)行transaction.replace(), 查看BackStackRecord,調(diào)用過程源碼如下:
我們發(fā)現(xiàn)隘弊,replace()最終調(diào)用的函數(shù)為doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd), 將Fragment和對Fragment所進(jìn)行的操作放到op鏈表中:
該函數(shù)首先設(shè)置fragment的mFragmentManager屬性哈踱,然后再設(shè)置其mContainerId和mFragmentId,最后創(chuàng)建Op對象梨熙,然設(shè)置相應(yīng)自段开镣,其中cmd自動(dòng)用來標(biāo)識事務(wù)的類型,分為如下幾類:
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è)字段的意思可直接通過英文名稱獲知咽扇。Op()類是BackStackRecord中聲明的結(jié)構(gòu)體哑子,本質(zhì)上是一個(gè)雙向鏈表的Node。addOp()如下:
該函數(shù)將Op對象添加到鏈表的末尾肌割,并將mNumOp的值增一卧蜓。
transaction.addToBackStack(null)設(shè)置了mAddToBackStack為true,源碼如下:
此函數(shù)將mAddToBackStack自段設(shè)置為true,并設(shè)置mName字段把敞。
最后調(diào)用transaction.commit()來執(zhí)行transaction弥奸。commit()的調(diào)用過程代碼如下:
由于mAddToBackStack為true,所以會(huì)用FragmentManager為BackstackRecorder也即FragmentTransaction分配一個(gè)index奋早,分配過程如下:
FragmentManager用mAvailBackStackIndices和mBackStackIndices兩個(gè)數(shù)組來為BackStackRecord分配Index盛霎。mAvailBackStackIndices用來存儲(chǔ)在mBackStackIndices中能夠分配的Index赠橙,mBackStackIndices則用來保存BackStackRecord。這利用兩個(gè)數(shù)組可以減少對mBackStackIndices的動(dòng)態(tài)分配大小的次數(shù)愤炸,是一個(gè)以空間換時(shí)間的策略期揪。上面的代碼首先判斷是否有可用的Index分配給BackStackRecord,若無則直接將BackStackRecord插入到mBackStackIndices;若存在的話則從mAvailBackStackIndices的隊(duì)尾取出一個(gè)index规个,然后設(shè)置mBackStackIndices中該index下的值凤薛。
讓我們回到commit()中,該函數(shù)最后執(zhí)行mManager.enqueAction(),源碼如下:
該函數(shù)首先進(jìn)行狀態(tài)監(jiān)測诞仓,查看該Fagment所在的Activity的生命周期是否處于Saving Activity之前缤苫,因?yàn)锳ctivity保存狀態(tài)往往是由用戶離開那個(gè)Activity所造成的,在此之后執(zhí)行commit會(huì)丟失一些狀態(tài)信息墅拭。針對這種情況活玲,可以使用commitAllowingStateLoss().最后將BackStackRecord加入到執(zhí)行隊(duì)列中。當(dāng)?shù)谝淮瓮鶊?zhí)行
隊(duì)列中添加消息時(shí)谍婉,首先會(huì)從消息隊(duì)列中所有callback屬性為mExecCommit的消息刪除舒憾,然后重新將mExecCommit添加到消息隊(duì)列。mExecCommit的定義如下:
execPendingActions()只能在主線程內(nèi)被調(diào)用穗熬,其內(nèi)部通過一個(gè)循環(huán)對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,而不是開個(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,我們來看看BackStackRecorder中的run(),如下所示:
addBackStackState()的源碼如下:
可以看到傳說中的BackStack就是在這里被創(chuàng)建的, FragmentManager中的BackStack主要是用來存儲(chǔ)FragmentTransaction的蝠嘉。
小結(jié):
FragmentTransaction中的Op鏈用來保存add最疆、remove、replace等action蚤告,在FragmentTransaction的run執(zhí)行時(shí)努酸,Op鏈會(huì)被變量以調(diào)整每個(gè)節(jié)點(diǎn)的內(nèi)容。
FragmentManager使用一個(gè)BackStack來管理FragmentTransaction杜恰;使用mAdded數(shù)組來添加被add的Fragment获诈,F(xiàn)ragment的創(chuàng)建仍源、顯示等行為都受FragmentManager的控制。
FragmentManager中的moveToState()是一個(gè)非常重要的函數(shù)舔涎,在FragmentTransaction run的時(shí)候被調(diào)用笼踩。下次我們將深入這個(gè)函數(shù)。