Recycler 只被RecyclerView持有杨帽。
Recycler 被回收的ViewHolder人柿,實(shí)際上是放入了RecyclerViewPool里面达传。
Recycler 只會(huì)操作ViewHolder碌更,不去操作具體的View瓶籽,即使在某些情況下傳入view恃鞋,也是通過view去找到與之對(duì)應(yīng)的holder崖媚。
Recycler的緩存分三級(jí)亦歉,一級(jí)緩存保存在列表的數(shù)據(jù),第二級(jí)是根據(jù)使用者是否配置來確定畅哑,三級(jí)緩存則是緩存池肴楷。
一級(jí)緩存分兩種,被廢棄的view(Scrap View)荠呐,被回收的View(RecycledView)
被廢棄的view(Scrap View)緩存
mAttachedScrap:其添加holder的地方為scrapView(); 條件 可重用的holder赛蔫、已移除的holder、失效的holder泥张、不是Update的holder呵恢。
mChangedScrap:與mAttachedScrap同樣在scrapView()里面添加數(shù)據(jù),只要不是上述情況就加入到這里面;
以上兩種方法都是在unscrapView()方法里面對(duì)緩存進(jìn)行移除媚创。判斷條件為holder.mInChangeScrap.
被回收的View(RecycledView)緩存
mCachedViews:添加緩存的方法recycleViewHolderInternal()瑰剃,根據(jù)holder 可回收或強(qiáng)制回收,同時(shí)要保證最大可緩存數(shù)大于0筝野,holder的標(biāo)志必須 是FLAG_INVALID晌姚、FLAG_REMOVED、FLAG_UPDATE歇竟、FLAG_ADAPTER_POSITION_UNKNOWN之一挥唠,則會(huì)加入到緩存列表mCachedViews;不符合上述條件焕议,那么則直接加入到RecycledViewPool緩存池宝磨。要注意的是,雖然mCachedViews是ArrayList盅安,但是它不是無限制了添加holder唤锉,有一個(gè)maxCachedSize對(duì)它做了限制。
而從mCachedViews里移除holder時(shí)别瞭,一般會(huì)將其添加到RecycledViewPool 回收池里窿祥。holder在加入緩存池的時(shí)候,holder會(huì)與所屬的RecyclerView解除關(guān)聯(lián)蝙寨,還會(huì)將holder所有設(shè)置過的數(shù)據(jù)設(shè)置回初始狀態(tài)晒衩,另一種移除的情況是加入到RecyclerView展示的情況。
那么RecyclerView 根據(jù)什么判定用廢棄還是回收呢墙歪?
看看recycleViewHolderInternal 與 scrapView 各自調(diào)用的地方. 最后在LayoutManger里找到了關(guān)鍵代碼听系,
RecyclerView.LayoutManger.scrapOrRecycleView(), 它調(diào)用鏈:onLayoutChildren()=> detachAndScrapAttachedViews() => scrapOrRecycleView()虹菲,
見下代碼:
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
...
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
很明顯靠胜,被回收的是:holder失效 && 未移除 && hasStableIds未設(shè)置為true,
其他情況,被廢棄浪漠。
其上還有兩個(gè)函數(shù)要關(guān)注一下:removeViewAt(index)菠赚、detachViewAt(index),他們的內(nèi)部都由ChildHelpter去執(zhí)行具體的操作郑藏,相同點(diǎn)都會(huì)從mBucket里將view移除衡查,不同點(diǎn)在于:removeViewAt(index)還會(huì)將index的view從mHiddenViews移除的動(dòng)作。 由于ChildHelper內(nèi)部維護(hù)了可見\不可見兩列表必盖,而scrap方式不去刪除不可見表的數(shù)據(jù)拌牲,因此其復(fù)用性要高一些,這也是后文獲取緩存的緩存為什么scrap在前的原因歌粥。
關(guān)于緩存池 RecyclerViewPool
緩存池塌忽,保存holder根據(jù)getItemViewType()類別分別保存,它也是有存儲(chǔ)個(gè)數(shù)限制的失驶,默認(rèn)每個(gè)type保存最大限制個(gè)數(shù)為5個(gè)土居。
mScrap:以type作為key 保存對(duì)應(yīng)類型的緩存列表。
ScrapData:緩存列表的載體嬉探,mScrapHeap才是真正的列表擦耀。同時(shí)它還保存了緩存的最大時(shí)長。
獲取緩存過程
前面分析了holder是怎么緩存的涩堤,下面看看它是怎么從各級(jí)緩存里面將數(shù)據(jù)拿出來的眷蜓。
關(guān)鍵代碼點(diǎn):Recycler.tryGetViewHolderForPositionByDeadline()
查找緩存順序
- 在預(yù)布局狀態(tài),如果有改變的, 就從 mChangeScrap里面找尋胎围。
- 沒到找吁系,根據(jù)位置position去找,從mAttachedScrap里面去找白魂,沒有還要去ChildHelper里面的不可見列表找汽纤,還找不到,去mCachedViews里面未失效找對(duì)應(yīng)的位置的holder福荸。
- 沒找到蕴坪,根據(jù)adapter.getItemId(),再從mAttachedScrap逞姿,再從mCachedViews里找辞嗡。
- 還沒找到,若配置了mViewCacheExtension對(duì)象滞造,則從這里去找,一定會(huì)找到栋烤,找不到就會(huì)throw谒养,若不配置則沒有該步驟。
- 還沒找到,最后到緩存池里找买窟,
- 最后都沒有找到丰泊,放棄治療了,自己去創(chuàng)建一個(gè)viewHolder始绍。
并不是這里找到了viewHolder就可以直接使用瞳购,需要對(duì)該holder設(shè)置一部分屬性:
holder.mPreLayoutPosition,如果是預(yù)布局狀態(tài)并且已綁定了view 才需要亏推;
bound:holder未與RecyclerView学赛、adapter綁定的情況,將mAdater與holder綁定吞杭,tryBindViewHolderByDeadline()盏浇。這種場景是從緩存池里取出的,因?yàn)樵诖嫒氲臅r(shí)候是解除了關(guān)系并將holder設(shè)置回了初始狀態(tài)芽狗,我們熟悉的ViewHolder.bindViewHolder()方法绢掰,在復(fù)用時(shí)就是這里調(diào)用的。