源碼解析之ListView

大家元旦快樂~

好記性不如爛筆頭,所以我準備弄個源碼解析系列渐夸,不準備詳細解析源碼嗓蘑,但把基本原理和設(shè)計思想梳理清楚,也給自己留個筆記存檔好在后面需要的時候翻起虫溜。

今天就從ListView開始。

ListView的核心在于layoutChildren函數(shù)股缸,分兩種情況吼渡,一種是全新加載,第二種是非全新加載乓序。主要區(qū)別在于ListView內(nèi)部的View緩存池的使用寺酪,下面依次來講下坎背。

在layoutChildren里面,會根據(jù)LayoutMode選擇調(diào)用fillSpecific/fillUp/fillFromTop之類的函數(shù)來進行填充寄雀,這個只是策略問題不是很關(guān)鍵得滤,最終這些函數(shù)都調(diào)用了makeAndAddView,然后再調(diào)用obtainView盒犹,再調(diào)用adapter.getView懂更,拿到view之后再使用setupChild加入到ListView里面去并放好位置(包含child自己的measure),流程如下:

setupChild函數(shù)里面片段:

全新加載的很好理解急膀,每個Item都是按照上面的流程走沮协,并且每個Item的view都是通過getView里面inflate出來的(這種情況getView的convertView傳過來是空,意味著ListView還沒有緩存view可以使用)

非全新加載卓嫂,比如頁面滑動慷暂,或者adapter數(shù)據(jù)發(fā)生變化,這種情況下面整體流程和全新加載沒有區(qū)別晨雳,但在部分函數(shù)調(diào)用里面有細微差別行瑞,比如:

layoutChildren里面首先將ListView的child都放入緩存池:

// Pull all children into the RecycleBin.

// These views will be reused if possible

final int firstPosition = mFirstPosition;

final RecycleBin recycleBin = mRecycler;

if (dataChanged) {

for (int i = 0; i < childCount; i++) {

recycleBin.addScrapView(getChildAt(i), firstPosition+i);

}

}else {

recycleBin.fillActiveViews(childCount, firstPosition);

}

緩存池管理就是這個RecycleBin對象,他里面有兩種緩存餐禁,一種叫ActiveViews血久,看上面代碼如果不是數(shù)據(jù)發(fā)生變化的非全新加載(比如頁面滾動),則把所有子view都放入ActiveViews帮非,然后重新計算位置重新擺放新的view的時候氧吐,就會首先從ActiveViews里面拿出緩存view,看makeAndAddView函數(shù)的第一段就是:

更巧妙的是末盔,在拿ActiveView的緩存view的時候副砍,會根據(jù)位置來拿,這樣的view認為是不需要重新經(jīng)過adapter的getView函數(shù)的庄岖,這樣極大的提高了效率。(頁面滾動的時候頁面里面的item只是位置變化角骤,不需要重新調(diào)用adapter.getView函數(shù))

View getActiveView(int position) {

int index = position -mFirstActivePosition;

final View[] activeViews =mActiveViews;

if (index >=0 && index < activeViews.length) {

final View match = activeViews[index];

activeViews[index] =null;

return match;

}

return null;

}

假如ActiveViews里面拿不到緩存view了隅忿,比如ListView高度發(fā)生了變化,需要更多的view來填充邦尊,這個時候就會從另外一種緩存里面拿背桐,叫做ScrapViews。這種緩存view是屬于ListView被填滿了蝉揍,結(jié)果還剩余有view就會被放入到這個緩存池里面來链峭。在layoutChildren函數(shù)的尾部可以看到有這樣一段代碼就是這個意思:

// Flush any cached views that did not get reused above

recycleBin.scrapActiveViews();

/**

* Move all views remaining in mActiveViews to mScrapViews.

*/

void scrapActiveViews() {

final View[] activeViews =mActiveViews;

final boolean hasListener =mRecyclerListener !=null;

final boolean multipleScraps =mViewTypeCount > 1;

ArrayList scrapViews =mCurrentScrap;

final int count = activeViews.length;

for (int i = count - 1; i >= 0; i--) {

.......

從ScrapViews拿緩存view的代碼在obtainView里面:

final View scrapView =mRecycler.getScrapView(position);

final View child =mAdapter.getView(position, scrapView,this);

if (scrapView !=null) {

if (child != scrapView) {

// Failed to re-bind the data, return scrap to the heap.

mRecycler.addScrapView(scrapView, position);

}else if (child.isTemporarilyDetached()) {

outMetadata[0] =true;

// Finish the temporary detach started in addScrapView().

child.dispatchFinishTemporaryDetach();

}

}

可以看到adapter.getView的第二個參數(shù)convertView就是從ScrapViews里面拿過來的緩存view,可能為空也可能不為空又沾,所以我們的getView函數(shù)都要有null判斷弊仪。

這樣子我們就能串起來了熙卡,在makeAndAddView里面先看看ActiveViews里面有沒有緩存view,有的話則直接成功返回也不需要調(diào)用adapter.getView励饵。沒有的話則繼續(xù)調(diào)用obtainView驳癌,然后再去ScrapViews里面看看有沒有緩存view,ScrapViews里面拿出來的view都需要重新經(jīng)過adapter.getView進行重新刷數(shù)據(jù)役听。ScrapViews可能拿到緩存view也可能拿不到颓鲜,所以getView實現(xiàn)需要null判斷。

我們可以把ActiveViews和ScrapViews理解為一二級緩存典予,有效率上面的差別(很明顯后者要經(jīng)過getView重新綁定數(shù)據(jù))

ListView的layoutChildren在開始的時候通過fillActiveViews將子view全部放到ActiveViews里面甜滨,然后等會重新布局的時候又首先從ActiveViews里面拿出來,不夠用的時候再繼續(xù)從ScrapViews里面拿瘤袖,非常高效衣摩。所以ActiveViews可以理解為layout期間的一個臨時產(chǎn)物。

整體流程就結(jié)束了孽椰,下面介紹下有些細節(jié)的地方可以從中學(xué)習(xí)到的昭娩。

View有onAttachedToWindow/onDetachFromWindow,還有一個不怎么常用的onStartTemporaryDetach/onFinishTemporaryDetach黍匾,表示開始和結(jié)束臨時Detach栏渺,這種狀態(tài)View其實沒有真正觸發(fā)onDetachFromWindow,只是臨時的Detached了锐涯,在addScrapView函數(shù)里看到有調(diào)用start磕诊,view被放入ScrapViews緩存池了,臨時被detach:

然后在obtainView里面纹腌,從ScrapViews里面重新拿出來要使用了霎终,再調(diào)用finish:

這個臨時detached挺有意思,結(jié)合ViewGroup的detachAllViewsFromParent(在layoutChildren里面一開始就會調(diào)用這個方法)升薯,讓子view的parent都設(shè)成null莱褒,可以達到一些很巧妙的實現(xiàn)。

ScrapViews里面的緩存view有的是真正onDetachFromWindow涎劈,有的則是onStartTemporaryDetach广凸,造成這個的原因主要是scrapActiveViews和addScrapView兩個實現(xiàn)上的差異,不過這個不影響實際使用蛛枚,obtainView里面會根據(jù)實際情況來決定拿到的緩存view是要重新attachToWindow還是finshTemporaryDetach谅海,這個outMetData[0](和mIsScrap[0]是同一個)就是標(biāo)記緩存view是不是之前已經(jīng)detachFromWiindow(之前是isTemporarilyDetached的,說明還未真正detachFromWindow)

然后setupChild里面會根據(jù)是不是attachedToWindow做不同的操作:重新指定parent(attachViewToParent)還是重新attachToWindow(addViewInLayout)

源碼解析就怕別人看不懂蹦浦,但愿對同學(xué)們有些幫助~

更多文章關(guān)注微信公眾號:安卓之美

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扭吁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侥袜,老刑警劉巖蝌诡,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異系馆,居然都是意外死亡送漠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門由蘑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闽寡,“玉大人,你說我怎么就攤上這事尼酿∫罚” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵裳擎,是天一觀的道長涎永。 經(jīng)常有香客問我,道長鹿响,這世上最難降的妖魔是什么羡微? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮惶我,結(jié)果婚禮上妈倔,老公的妹妹穿的比我還像新娘。我一直安慰自己绸贡,他們只是感情好盯蝴,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著听怕,像睡著了一般捧挺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尿瞭,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天闽烙,我揣著相機與錄音,去河邊找鬼声搁。 笑死黑竞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酥艳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼爬骤,長吁一口氣:“原來是場噩夢啊……” “哼充石!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霞玄,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤骤铃,失蹤者是張志新(化名)和其女友劉穎拉岁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惰爬,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡喊暖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撕瞧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陵叽。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丛版,靈堂內(nèi)的尸體忽然破棺而出巩掺,到底是詐尸還是另有隱情,我是刑警寧澤页畦,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布胖替,位于F島的核電站,受9級特大地震影響豫缨,放射性物質(zhì)發(fā)生泄漏独令。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一好芭、第九天 我趴在偏房一處隱蔽的房頂上張望燃箭。 院中可真熱鬧,春花似錦栓撞、人聲如沸遍膜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓢颅。三九已至,卻和暖如春弛说,著一層夾襖步出監(jiān)牢的瞬間挽懦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工木人, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留信柿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓醒第,卻偏偏與公主長得像渔嚷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稠曼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容