一、Fragmentation是什么?
Fragmentation是一款以解決多Activity+多Fragment或單Activity+多Fragment復(fù)雜嵌套問(wèn)題岔激,快速開(kāi)發(fā)出基于Fragment的app的框架让歼。包括狀態(tài)保存恢復(fù)、生命周期監(jiān)聽(tīng)复凳、轉(zhuǎn)場(chǎng)動(dòng)畫(huà)批销、啟動(dòng)模式洒闸、滑動(dòng)返回、懶加載以及startForResult等功能均芽。
以下是原文介紹:
為"單Activity + 多Fragment的架構(gòu)","多模塊Activity + 多Fragment的架構(gòu)"而生丘逸,幫你簡(jiǎn)化使用過(guò)程,輕松解決各種復(fù)雜嵌套等問(wèn)題掀宋,修復(fù)了官方Fragment庫(kù)存在的一些BUG深纲。
原文地址:
Fragment之我的解決方案:Fragmentation - 簡(jiǎn)書(shū)。
本文將著重介紹其中Fragment啟動(dòng)模式功能劲妙、startForResult湃鹊、懶加載以及回退功能。
之所以介紹該框架镣奋,主要在實(shí)際工作中項(xiàng)目的架構(gòu)也是采用類(lèi)似Fragment自定義View構(gòu)成的UI框架形式如單Activity+多Fragment币呵,只是這里Fragment是自定義View,其中堆棧跳轉(zhuǎn)有值得優(yōu)化的部分侨颈,而Fragmentation給了我一些啟發(fā)余赢,所以本文重點(diǎn)介紹上述功能。
二哈垢、啟動(dòng)模式妻柒。
Activity啟動(dòng)模式分為四種:
1.standard:
每次啟動(dòng)都會(huì)創(chuàng)建一個(gè)新的實(shí)例,無(wú)論這個(gè)實(shí)例是否已經(jīng)存在耘分,在這種模式下誰(shuí)啟動(dòng)了Activity A,那這個(gè)A就會(huì)運(yùn)行在啟動(dòng)它的那個(gè)Activity所在的任務(wù)棧里举塔。(圖片引用:https://www.cnblogs.com/claireyuancy/p/7387696.html)
2.singleTop
和standard模式類(lèi)似,不會(huì)創(chuàng)建新的task求泰,都是在原任務(wù)棧中創(chuàng)建央渣,也就是該Activity實(shí)例在當(dāng)前棧頂,那么不會(huì)創(chuàng)建新的實(shí)例渴频,并調(diào)用onNewIntent方法痹屹。但是如果存在該實(shí)例卻不在棧頂或不存在該實(shí)例都會(huì)新建一個(gè)新的實(shí)例出來(lái)。
3.singleTask
singleTask相對(duì)比較復(fù)雜枉氮,先介紹最簡(jiǎn)單的情形:
在任務(wù)棧T3里志衍,存在ADBC三個(gè)Activity,這個(gè)時(shí)候如果啟動(dòng)D聊替,且所需任務(wù)棧是T3楼肪,根據(jù)棧內(nèi)復(fù)用原則D不會(huì)重新創(chuàng)建,而是將D之上的Activity彈出棧惹悄,將D置為棧頂春叫,表示為AD,B,C被彈出。(圖片來(lái)源:http://www.reibang.com/p/2a9fcf3c11e4)
再比如如果啟動(dòng)D暂殖,且所需堆棧是T2价匠,由于D和T2都不存在,系統(tǒng)會(huì)創(chuàng)建T2和D呛每,并將D壓入T2.
還有一種情況啟動(dòng)D且所需堆棧T1踩窖,由于D不存在則創(chuàng)建新的實(shí)例并壓入T1.
相對(duì)復(fù)雜的情形:
前后臺(tái)任務(wù)棧,前臺(tái)任務(wù)棧包含AB兩個(gè)activity實(shí)例晨横,后臺(tái)任務(wù)棧包含CD兩個(gè)activity洋腮,如果這個(gè)時(shí)候前臺(tái)任務(wù)棧啟動(dòng)D那么整個(gè)后臺(tái)任務(wù)棧都會(huì)放到前臺(tái),前臺(tái)的堆棧順序表示為ABCD手形,D在頂層啥供。
如果啟動(dòng)的是C,那么前臺(tái)堆棧順序?yàn)锳BC库糠,D被直接出棧了伙狐。
最后一種情況,A,B兩個(gè)應(yīng)用瞬欧,C是B的一個(gè)Activity贷屎,且C的allowtaskReparening屬性為true,那么當(dāng)A應(yīng)用啟動(dòng)B應(yīng)用的C時(shí)黍判,就會(huì)將C放在B的任務(wù)棧頂層,再描述的清楚一些就是篙梢,啟動(dòng)C以后按home鍵回到桌面顷帖,再點(diǎn)擊B應(yīng)用圖標(biāo),這個(gè)時(shí)候展示的不是B應(yīng)用的主Activity而是C渤滞。
4singleinstance贬墩。
就是加強(qiáng)版singleTask模式,使用該模式就是啟動(dòng)一個(gè)新的任務(wù)棧妄呕,且這個(gè)任務(wù)棧里只能存在一個(gè)Activity實(shí)例陶舞。
那么了解了Activity的啟動(dòng)模式以后我們?cè)賮?lái)看Fragmentation是如何實(shí)現(xiàn)Fragment的啟動(dòng)模式的。
Fragmentation只實(shí)現(xiàn)了singleTop和singleTask绪励,我們來(lái)關(guān)注下他是如何實(shí)現(xiàn)的肿孵。
FragmentManager管理堆棧原理
2.1Fragmentation singleTop模式
@Override
public void start(finalSupportFragment toFragment,@LaunchModefinal int launchMode) {
mFragmentationDelegate.dispatchStartTransaction(getFragmentManager(), this,toFragment,0,launchMode,FragmentationDelegate.TYPE_ADD);
}
首先在supportFragment(備注:基礎(chǔ)庫(kù)中的需要被繼承實(shí)現(xiàn)的基類(lèi))中實(shí)現(xiàn)了start方法用于啟動(dòng)新的fragment,內(nèi)部實(shí)現(xiàn)是由FragmentationDelegate代理實(shí)現(xiàn),進(jìn)一步看:
if(handleLaunchMode(fragmentManager,to,toFragmentTag,launchMode))return;
在dispatchStartTransaction方法中有這么一段代碼疏魏,用于處理啟動(dòng)模式的停做,我們?cè)龠M(jìn)一步進(jìn)去看
SupportFragment topFragment = getTopFragment(fragmentManager);//找到棧頂Fragment
Fragment stackToFragment = findStackFragment(toFragment.getClass(),toFragmentTag,fragmentManager);//再?gòu)亩褩V姓业絫oFragment
if(launchMode == SupportFragment.SINGLETOP) {
// 在棧頂
if(toFragment == topFragment || toFragment.getClass().getName().equals(topFragment.getClass().getName())) {
handleNewBundle(toFragment,stackToFragment);
return true;
}
}
可以看到當(dāng)從堆棧中找到所要跳轉(zhuǎn)的toFragment以后且toFragment也是TopFragment那么就會(huì)開(kāi)始處理newBundle,并在handleNewBundle中使用stackToFragment調(diào)用onNewBundle方法
private void handleNewBundle(SupportFragment toFragment,Fragment stackToFragment) {
Bundle argsNewBundle = toFragment.getNewBundle();
Bundle args = toFragment.getArguments();
if(args.containsKey(FRAGMENTATION_ARG_CONTAINER)) {
args.remove(FRAGMENTATION_ARG_CONTAINER);
}
if(argsNewBundle !=null) {
args.putAll(argsNewBundle);
}
((SupportFragment) stackToFragment).onNewBundle(args);
}
2.2 Fragmentation singTask模式
在handleLanuchMode方法執(zhí)行以前都一樣大莫,我們來(lái)看該方法完整部分
private boolean handleLaunchMode(FragmentManager fragmentManager,SupportFragment toFragment,String toFragmentTag, intlaunchMode) {
SupportFragment topFragment = getTopFragment(fragmentManager);
if(topFragment ==null)return false;
Fragment stackToFragment = findStackFragment(toFragment.getClass(),toFragmentTag,fragmentManager);
if(stackToFragment ==null)return false;
if(launchMode == SupportFragment.SINGLETOP) {
// 在棧頂
if(toFragment == topFragment || toFragment.getClass().getName().equals(topFragment.getClass().getName())) {
handleNewBundle(toFragment,stackToFragment);
return true;
}
}else if(launchMode == SupportFragment.SINGLETASK) {
popToFix(toFragmentTag,0,fragmentManager);//這里是singleTask方法執(zhí)行關(guān)鍵
handleNewBundle(toFragment,stackToFragment);
return true;
}
return false;
}
它與singleTop不同的地方在于會(huì)執(zhí)行popstack操作蛉腌。具體我們來(lái)看popToFix方法。
private void popToFix(String fragmentTag, intflag, finalFragmentManager fragmentManager) {
if(fragmentManager.getFragments() ==null)return;
mActivity.preparePopMultiple();
fragmentManager.popBackStackImmediate(fragmentTag,flag);//執(zhí)行回退棧立即執(zhí)行
mActivity.popFinish();
mHandler.post(newRunnable() {
@Override
public voidrun() {
FragmentTransactionBugFixHack.reorderIndices(fragmentManager);
}
});
}
當(dāng)執(zhí)行完pop操作以后,后續(xù)的handleNewBundle方法操作一致烙丛。
2.3 Fragmentation startForResult
使用startForResult啟動(dòng)Fragment時(shí)會(huì)在dispatchStartTransaction存儲(chǔ)請(qǐng)求requestcode舅巷,用于回調(diào)
/**
* save requestCode
*/
private void saveRequestCode(Fragment to,? int requestCode) {
Bundle bundle = to.getArguments();
if(bundle ==null) {
bundle =newBundle();
to.setArguments(bundle);
}
ResultRecord resultRecord =newResultRecord();
resultRecord.requestCode= requestCode;
bundle.putParcelable(FRAGMENTATION_ARG_RESULT_RECORD,resultRecord);
}
啟動(dòng)后在需要返回的響應(yīng)碼的fragment里存儲(chǔ)返回?cái)?shù)據(jù)并返回resultcode,舉個(gè)例子:
mBtnModify.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View v) {
Bundle bundle =newBundle();
bundle.putString(DetailFragment.KEY_RESULT_TITLE,mEtModiyTitle.getText().toString());
setFragmentResult(RESULT_OK,bundle);
Toast.makeText(_mActivity,"修改成功!",Toast.LENGTH_SHORT).show();
}
});
當(dāng)該Fragment銷(xiāo)毀時(shí)河咽,在supportFragment的onDestroy方法中會(huì)處理返回結(jié)果數(shù)據(jù),當(dāng)然還是由代理類(lèi)FragmentationDelegate處理
void handleResultRecord(Fragment from) {
SupportFragment preFragment = getPreFragment(from);
if(preFragment ==null)return;
Bundle args = from.getArguments();
if(args ==null|| !args.containsKey(FRAGMENTATION_ARG_RESULT_RECORD))return;
ResultRecord resultRecord = args.getParcelable(FRAGMENTATION_ARG_RESULT_RECORD);
if(resultRecord ==null)return;
preFragment.onFragmentResult(resultRecord.requestCode,resultRecord.resultCode,resultRecord.resultBundle);
}
通過(guò)當(dāng)前的fragment找到前一個(gè)fragment也就是啟動(dòng)它的fragment钠右,注意startForResult使用的是standard模式,然后調(diào)用preFragmentResult的onFragmentResult方法將序列化的bundle數(shù)據(jù)傳回操作即可库北。
2.4Fragmentation懶加載
supportFragment的onLazyInitView方法主要用于數(shù)據(jù)的懶加載爬舰,view的初始化還是在createView時(shí)候完成。之所以采用懶加載是因?yàn)楫?dāng)類(lèi)似微信這種多tab切換的fragment每個(gè)頁(yè)面都有可能請(qǐng)求大量數(shù)據(jù)寒瓦,而用戶沒(méi)有點(diǎn)擊顯示卻要耗費(fèi)資源去請(qǐng)求情屹,用戶體驗(yàn)會(huì)比較差。
onLazyInitView在當(dāng)前view可視的時(shí)候開(kāi)始加載杂腰。具體來(lái)看源碼:
private void dispatchSupportVisible(boolean visible) {
.....//不重要代碼
if(visible) {
mSupportFragment.onSupportVisible();
if(mIsFirstVisible) {
mIsFirstVisible=false;
mSupportFragment.onLazyInitView(mSaveInstanceState);
}
}
.....//不重要代碼
}
上面的方法是VisibleDelegate中分發(fā)可視事件的方法垃你,其中當(dāng)?shù)谝淮慰梢晻r(shí)會(huì)回調(diào)懶加載,接下來(lái)喂很,
而VisibleDelegate會(huì)代理所有的生命周期方法惜颇,在相應(yīng)的生命周期代理中會(huì)處理分發(fā)相應(yīng)的可視性,最終調(diào)用dispatchSupportVisible方法少辣。
隨便舉個(gè)例子:
public void onResume() {
if(!mIsFirstVisible) {
if(!mIsSupportVisible&& !mInvisibleWhenLeave&& isFragmentVisible(mSupportFragment)) {
mNeedDispatch=false;
dispatchSupportVisible(true);
}
}
}
VisibleDelegate的onResume方法里對(duì)dispatchSupportVisible方法的調(diào)用凌摄,而onResume又會(huì)在supportFragment的onResume方法中被調(diào)用。
2.5FragmentManager對(duì)回退棧管理漓帅。
FragmentManager在新的Fragment創(chuàng)建時(shí)會(huì)分配給一個(gè)Fragment下標(biāo)锨亏,同時(shí)由mActive管理當(dāng)前所有激活狀態(tài)的Fragment。不過(guò)回退時(shí)mActive是對(duì)相應(yīng)index置空而不是remove忙干,需要注意的是
void makeInactive(Fragment f) {
????if (f.mIndex < 0) { return; }
????if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
????mActive.set(f.mIndex, null);
????if (mAvailIndices == null) {
????????mAvailIndices = new ArrayList();
????}
????mAvailIndices.add(f.mIndex);
????mHost.inactivateFragment(f.mWho);
????f.initState();
}
添加新的fragment源碼
void makeActive(Fragment f) {
????if (f.mIndex >= 0) { return; }
????if (mAvailIndices == null || mAvailIndices.size() <= 0) {
????????if (mActive == null) { mActive = new ArrayList();
????}
????f.setIndex(mActive.size(), mParent); mActive.add(f);
????} else {
????????f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
????????mActive.set(f.mIndex, f);
????}
????if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
需要注意的是mAvailIndices在pop多個(gè)Fragment時(shí)候器予,有可能會(huì)出現(xiàn)如下情況:
比如:正常堆棧順序?yàn)锳,B,C,D,現(xiàn)在Pop出C和D捐迫,堆棧變更為A,B,null,null乾翔,這個(gè)時(shí)候如果push新的Fragment,有可能會(huì)出現(xiàn)A,B,null,C的情況施戴,原因是由于mAvailIndices在記錄popFragment的時(shí)候反浓,有可能先保存的較小的index后再保存較大index,也就是mAvailIndices里的Fragment的index順序無(wú)法保證赞哗,這個(gè)時(shí)候需要做一次降序排列以解決問(wèn)題