RecyclerView
做爲一個很是惹人愛的控件,有一部分的功勞歸於它優秀的緩存機制。RecyclerView
的緩存機制屬於RecyclerView
的核心部分,同時也是比較難的部分。儘管緩存機制那麼難,可是仍是不能抵擋得住咱們的好奇心😂。今天咱們來看看它的神奇之處。數組
本文參考資料:緩存
因爲本文跟本系列的前兩篇文章都有關聯,因此爲了便於理解,能夠去看做者本系列的前兩篇文章。bash
注意,本文全部的代碼都來自於27.1.1。數據結構
在正式分析源碼以前,我先對緩存機制作一個概述,同時也會對一些概念進行統一解釋,這些對後面的分析有很大的幫助,由於若是不理解這些概念的話,後面容易看得雨裏霧裏的。ide
首先,我將RecyclerView
的緩存分爲四級,可能有的人將它分爲三級,這些看我的的理解。這裏統一說明一下每級緩存的意思。源碼分析
緩存級別 | 實際變量 | 含義 |
---|---|---|
一級緩存 | mAttachedScrap 和mChangedScrap |
這是優先級最高的緩存,RecyclerView 在獲取ViewHolder 時,優先會到這兩個緩存來找。其中mAttachedScrap 存儲的是當前還在屏幕中的ViewHolder ,mChangedScrap 存儲的是數據被更新的ViewHolder ,好比說調用了Adapter 的notifyItemChanged 方法。可能有人對這兩個緩存仍是有點疑惑,不要急,待會會詳細的解釋。 |
二級緩存 | mCachedViews |
默認大小爲2,一般用來存儲預取的ViewHolder ,同時在回收ViewHolder 時,也會可能存儲一部分的ViewHolder ,這部分的ViewHolder 一般來講,意義跟一級緩存差很少。 |
三級緩存 | ViewCacheExtension |
自定義緩存,一般用不到,在本文中先忽略 |
四級緩存 | RecyclerViewPool |
根據ViewType 來緩存ViewHolder ,每一個ViewType 的數組大小爲5,能夠動態的改變。 |
如上表,統一的解釋了每一個緩存的含義和做用。在這裏,我再來對其中的幾個緩存作一個詳細的解釋。佈局
mAttachedScrap
:上表中說,它表示存儲的是當前還在屏幕中ViewHolder
。其實是從屏幕上分離出來的ViewHolder
,可是又即將添加到屏幕上去的ViewHolder
。好比說,RecyclerView
上下滑動,滑出一個新的Item
,此時會從新調用LayoutManager
的onLayoutChildren
方法,從而會將屏幕上全部的ViewHolder
先scrap
掉(含義就是廢棄掉),添加到mAttachedScrap
裏面去,而後在從新佈局每一個ItemView
時,會從優先mAttachedScrap
裏面獲取,這樣效率就會很是的高。這個過程不會從新onBindViewHolder
。mCachedViews
:默認大小爲2,不過一般是3,3由默認的大小2 + 預取的個數1。因此在RecyclerView
在首次加載時,mCachedViews
的size
爲3(這裏以LinearLayoutManager
的垂直佈局爲例)。一般來講,能夠經過RecyclerView
的setItemViewCacheSize
方法設置大小,可是這個不包括預取大小;預取大小經過LayoutManager
的setItemPrefetchEnabled
方法來控制。
咱們在看RecyclerView
的源碼時,可能處處都能看到調用ViewHolder
的isInvalid
、isRemoved
、isBound
、isTmpDetached
、isScrap
和isUpdated
這幾個方法。這裏我統一的解釋一下。post
方法名 | 對應的Flag | 含義或者狀態設置的時機 |
---|---|---|
isInvalid |
FLAG_INVALID |
表示當前ViewHolder 是否已經失效。一般來講,在3種狀況下會出現這種狀況:1.調用了Adapter 的notifyDataSetChanged 方法;2. 手動調用RecyclerView 的invalidateItemDecorations 方法;3. 調用RecyclerView 的setAdapter 方法或者swapAdapter 方法。 |
isRemoved |
FLAG_REMOVED |
表示當前的ViewHolder 是否被移除。一般來講,數據源被移除了部分數據,而後調用Adapter 的notifyItemRemoved 方法。 |
isBound |
FLAG_BOUND |
表示當前ViewHolder 是否已經調用了onBindViewHolder 。 |
isTmpDetached |
FLAG_TMP_DETACHED |
表示當前的ItemView 是否從RecyclerView (即父View )detach 掉。一般來講有兩種狀況下會出現這種狀況:1.手動了RecyclerView 的detachView 相關方法;2. 在從mHideViews 裏面獲取ViewHolder ,會先detach 掉這個ViewHolder 關聯的ItemView 。這裏又多出來一個mHideViews ,待會我會詳細的解釋它是什麼。 |
isScrap |
無Flag來表示該狀態,用mScrapContainer 是否爲null來判斷 |
表示是否在mAttachedScrap 或者mChangedScrap 數組裏面,進而表示當前ViewHolder 是否被廢棄。 |
isUpdated |
FLAG_UPDATE |
表示當前ViewHolder 是否已經更新。一般來講,在3種狀況下會出現狀況:1.isInvalid 方法存在的三種狀況;2.調用了Adapter 的onBindViewHolder 方法;3. 調用了Adapter 的notifyItemChanged 方法 |
在四級緩存中,咱們並無將mHiddenViews
算入其中。由於mHiddenViews
只在動畫期間纔會有元素,當動畫結束了,天然就清空了。因此mHiddenViews
並不算入4級緩存中。fetch
這裏還有一個問題,就是上面在解釋mChangedScrap
時,也在說,當調用Adapter
的notifyItemChanged
方法,會將更新了的ViewHolder
反放入mChangedScrap
數組裏面。那究竟是放入mChangedScrap
仍是mHiddenViews
呢?同時可能有人對mChangedScrap
和mAttachedScrap
有疑問,這裏我作一個統一的解釋:動畫
首先,若是調用了
Adapter
的notifyItemChanged
方法,會從新回調到LayoutManager
的onLayoutChildren
方法裏面,而在onLayoutChildren
方法裏面,會將屏幕上全部的ViewHolder
回收到mAttachedScrap
和mChangedScrap
。這個過程就是將ViewHolder
分別放到mAttachedScrap
和mChangedScrap
,而什麼條件下放在mAttachedScrap
,什麼條件放在mChangedScrap
,這個就是他們倆的區別。
接下來咱們來看一段代碼,就能分清mAttachedScrap
和mChangedScrap
的區別了
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
throw new IllegalArgumentException("Called scrap view with an invalid view."
+ " Invalid views cannot be reused from scrap, they should rebound from"
+ " recycler pool." + exceptionLabel());
}
holder.setScrapContainer(this, false);
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
mChangedScrap.add(holder);
}
}
複製代碼
可能不少人初次看到這方法時,會很是的懵逼,我也是如此。今天咱們就來看看這個方法。這個根本的目的就是,判斷ViewHolder
的flag狀態,從而來決定是放入mAttachedScrap
仍是mChangedScrap
。從上面的代碼,咱們得出:
mAttachedScrap
裏面放的是兩種狀態的ViewHolder
:1.被同時標記爲remove
和invalid
;2.徹底沒有改變的ViewHolder
。這裏還有第三個判斷,這個跟RecyclerView
的ItemAnimator
有關,若是ItemAnimator
爲空或者ItemAnimator
的canReuseUpdatedViewHolder
方法爲true,也會放入到mAttachedScrap
。那正常狀況下,什麼狀況返回爲true呢?從SimpleItemAnimator
的源碼能夠看出來,當ViewHolder
的isInvalid
方法返回爲true時,會放入到mAttachedScrap
裏面。也就是說,若是ViewHolder
失效了,也會放到mAttachedScrap
裏面。- 那麼
mChangedScrap
裏面放什麼類型flag的ViewHolder
呢?固然是ViewHolder
的isUpdated
方法返回爲true時,會放入到mChangedScrap
裏面去。因此,調用Adapter
的notifyItemChanged
方法時,而且RecyclerView
的ItemAnimator
不爲空,會放入到mChangedScrap
裏面。
瞭解了mAttachedScrap
和mChangedScrap
的區別以後,接下咱們來看Scrap
數組和mHiddenViews
的區別。
mHiddenViews
只存放動畫的ViewHolder
,動畫結束了天然就清空了。之因此存在mHiddenViews
這個數組,我猜想是存在動畫期間,進行復用的可能性,此時就能夠在mHiddenViews
進行復用了。而Scrap
數組跟mHiddenViews
二者徹底不衝突,因此存在一個ViewHolder
同時在Scrap
數組和mHiddenViews
的可能性。可是這並不影響,由於在動畫結束時,會從mHiddenViews
裏面移除。
本文在分析RecyclerView
的換出機制時,打算從兩個大方面入手:1.複用;2.回收。
咱們先來看看複用的部分邏輯,由於只有理解了RecyclerView
到底是如何複用的,對回收才能更加明白。
RecyclerView
對ViewHolder
的複用,咱們得從LayoutState
的next
方法開始。LayoutManager
在佈局itemView
時,須要獲取一個ViewHolder
對象,就是經過這個方法來獲取,具體的複用邏輯也是在這個方面開始調用的。咱們來看看:
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
複製代碼
next
方法裏面其實也沒作什麼事,就是調用RecyclerView
的getViewForPosition
方法來獲取一個View
的。而getViewForPosition
方法最終會調用到RecyclerView
的tryGetViewHolderForPositionByDeadline
方法。因此,RecyclerView
真正複用的核心就在這個方法,咱們今天來詳細的分析一下這個方法。
經過這種方式來獲取優先級比較高,由於每一個ViewHolder
還沒被改變,一般在這種狀況下,都是某一個ItemView
對應的ViewHolder
被更新致使的,因此在屏幕上其餘的ViewHolder
,能夠快速對應原來的ItemView
。咱們來看看相關的源碼。
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
if (holder != null) {
if (!validateViewHolderForOffsetPosition(holder)) {
// recycle holder (and unscrap if relevant) since it can't be used if (!dryRun) { // we would like to recycle this but need to make sure it is not used by // animation logic etc. holder.addFlags(ViewHolder.FLAG_INVALID); if (holder.isScrap()) { removeDetachedView(holder.itemView, false); holder.unScrap(); } else if (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } } 複製代碼
如上的代碼分爲兩步:
- 從
mChangedScrap
裏面去獲取ViewHolder
,這裏面存儲的是更新的ViewHolder
。- 分別
mAttachedScrap
、mHiddenViews
、mCachedViews
獲取ViewHolder
咱們來簡單的分析一下這兩步。先來看看第一步。
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
複製代碼
若是當前是預佈局階段,那麼就從mChangedScrap
裏面去獲取ViewHolder
。那什麼階段是預佈局階段呢?這裏我對預佈局這個概念簡單的解釋。
預佈局又能夠稱之爲
preLayout
,噹噹前的RecyclerView
處於dispatchLayoutStep1
階段時,稱之爲預佈局;dispatchLayoutStep2
稱爲真正佈局的階段;dispatchLayoutStep3
稱爲postLayout
階段。同時要想真正開啓預佈局,必須有ItemAnimator
,而且每一個RecyclerView
對應的LayoutManager
必須開啓預處理動畫
。
是否是感受聽了解釋以後更加的懵逼了?爲了解釋一個概念,反而引出了更多的概念了?關於動畫的問題,不出意外,我會在下一篇文章分析,本文就不對動畫作過多的解釋了。在這裏,爲了簡單,只要RecyclerView
處於dispatchLayoutStep1
,咱們就當作它處於預佈局階段。
爲何只在預佈局的時候才從mChangedScrap
裏面去取呢? 首先,咱們得知道mChangedScrap
數組裏面放的是什麼類型的 ViewHolder
。從前面的分析中,咱們知道,只有當ItemAnimator
不爲空,被changed的ViewHolder
會放在mChangedScrap
數組裏面。由於chang動畫先後相同位置上的ViewHolder
是不一樣的,因此當預佈局時,從mChangedScrap
緩存裏面去,而正式佈局時,不會從mChangedScrap
緩存裏面去,這就保證了動畫先後相同位置上是不一樣的ViewHolder
。爲何要保證動畫先後是不一樣的ViewHolder
呢?這是RecyclerView
動畫機制相關的知識,這裏就不詳細的解釋,後續有專門的文章來分析它,在這裏,咱們只須要記住,chang動畫執行的有一個前提就是動畫先後是不一樣的ViewHolder
。
而後,咱們再來看看第二步。
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
if (holder != null) {
if (!validateViewHolderForOffsetPosition(holder)) {
// recycle holder (and unscrap if relevant) since it can't be used if (!dryRun) { // we would like to recycle this but need to make sure it is not used by // animation logic etc. holder.addFlags(ViewHolder.FLAG_INVALID); if (holder.isScrap()) { removeDetachedView(holder.itemView, false); holder.unScrap(); } else if (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } } 複製代碼
這一步理解起來比較容易,分別從mAttachedScrap
、 mHiddenViews
、mCachedViews
獲取ViewHolder
。可是咱們須要的是,若是獲取的ViewHolder
是無效的,得作一些清理操做,而後從新放入到緩存裏面,具體對應的緩存就是mCacheViews
和RecyclerViewPool
。recycleViewHolderInternal
方法就是回收ViewHolder
的方法,後面再分析回收相關的邏輯會重點分析這個方法,這裏就不進行追究了。
前面分析了經過Position的方式來獲取ViewHolder
,這裏咱們來分析一下第二種方式--ViewType
。不過在這裏,我先對前面的方式作一個簡單的總結,RecyclerView
經過Position
來獲取ViewHolder
,並不須要判斷ViewType
是否合法,由於若是可以經過Position
來獲取ViewHolder
,ViewType
自己就是正確對應的。
而這裏經過ViewType
來獲取ViewHolder
表示,此時ViewHolder
緩存的Position
已經失效了。ViewType
方式來獲取ViewHolder
的過程,我將它分爲3步:
- 若是
Adapter
的hasStableIds
方法返回爲true,優先經過ViewType
和id
兩個條件來尋找。若是沒有找到,那麼就進行第2步。- 若是
Adapter
的hasStableIds
方法返回爲false,在這種狀況下,首先會在ViewCacheExtension
裏面找,若是尚未找到的話,最後會在RecyclerViewPool
裏面來獲取ViewHolder。- 若是以上的複用步驟都沒有找到合適的
ViewHolder
,最後就會調用Adapter
的onCreateViewHolder
方法來建立一個新的ViewHolder
。
在這裏,咱們須要注意的是,上面的第1步 和 第2步有前提條件,就是兩個都必須比較ViewType
。接下來,我經過代碼簡單的分析一下每一步。
經過id尋找合適的ViewHolder
主要是經過調用getScrapOrCachedViewForId
方法來實現的,咱們簡單的看一下代碼:
// 2) Find from scrap/cache via stable ids, if exists
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
if (holder != null) {
// update position
holder.mPosition = offsetPosition;
fromScrapOrHiddenOrCache = true;
}
}
複製代碼
而getScrapOrCachedViewForId
方法自己沒有什麼分析的必要,就是分別從mAttachedScrap
和mCachedViews
數組尋找合適的ViewHolder
。
ViewCacheExtension
存在的狀況是很是的少見,這裏爲了簡單,就不展開了(實際上我也不懂!),因此這裏,咱們直接來看RecyclerViewPool
方式。
在這裏,咱們須要瞭解RecyclerViewPool
的數組結構。咱們簡單的分析一下RecyclerViewPool
這個類。
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
複製代碼
在RecyclerViewPool
的內部,使用SparseArray
來存儲每一個ViewType
對應的ViewHolder
數組,其中每一個數組的最大size爲5。這個數據結構是否是很是簡單呢?
簡單的瞭解了RecyclerViewPool
的數據結構,接下來咱們來看看複用的相關的代碼:
if (holder == null) { // fallback to pool
if (DEBUG) {
Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
+ position + ") fetching from shared pool");
}
holder = getRecycledViewPool().getRecycledView(type);
if (holder != null) {
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
複製代碼
相信這段代碼不用我來分析吧,表達的意思很是簡單。
if (holder == null) {
long start = getNanoTime();
if (deadlineNs != FOREVER_NS
&& !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
// abort - we have a deadline we can't meet return null; } holder = mAdapter.createViewHolder(RecyclerView.this, type); if (ALLOW_THREAD_GAP_WORK) { // only bother finding nested RV if prefetching RecyclerView innerView = findNestedRecyclerView(holder.itemView); if (innerView != null) { holder.mNestedRecyclerView = new WeakReference<>(innerView); } } long end = getNanoTime(); mRecyclerPool.factorInCreateTime(type, end - start); if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder"); } } 複製代碼
上面的代碼主要的目的就是調用Adapter
的createViewHolder
方法來建立一個ViewHolder
,在這個過程就是簡單計算了建立一個ViewHolder
的時間。
關於複用機制的理解,咱們就到此爲止。其實RecyclerView
的複用機制一點都不復雜,我以爲讓你們望而卻步的緣由,是由於咱們不知道爲何在這麼作,若是瞭解這麼作的緣由,一切都顯得那麼理所固然。
分析RecyclerView
的複用部分,接下來,咱們來分析一下回收部分。
回收是RecyclerView
複用機制內部很是重要。首先,有複用的過程,確定就有回收的過程;其次,同時理解了複用和回收兩個過程,這能夠幫助咱們在宏觀上理解RecyclerView
的工做原理;最後,理解RecyclerView
在什麼時候會回收ViewHolder
,這對使用RecyclerView
有很大的幫助。
其實回收的機制也沒有想象中那麼的難,本文打算從幾個方面來分析RecyclerView
的回收過程。
- scrap數組
- mCacheViews數組
- mHiddenViews數組
- RecyclerViewPool數組
接下來,咱們將一一的分析。
關於ViewHolder
回收到scrap
數組裏面,其實我在前面已經簡單的分析了,重點就在於Recycler
的scrapView
方法裏面。咱們來看看scrapView
在哪裏被調用了。有以下兩個地方:
- 在
getScrapOrHiddenOrCachedHolderForPosition
方法裏面,若是從mHiddenViews
得到一個ViewHolder
的話,會先將這個ViewHolder
從mHiddenViews
數組裏面移除,而後調用Recycler
的scrapView
方法將這個ViewHolder
放入到scrap
數組裏面,而且標記FLAG_RETURNED_FROM_SCRAP
和FLAG_BOUNCED_FROM_HIDDEN_LIST
兩個flag。- 在
LayoutManager
裏面的scrapOrRecycleView
方法也會調用Recycler
的scrapView
方法。而有兩種情形下會出現如此狀況:1. 手動調用了LayoutManager
相關的方法;2.RecyclerView
進行了一次佈局(調用了requestLayout
方法)
mCacheViews
數組做爲二級緩存,回收的路徑相較於一級緩存要多。關於mCacheViews數組,重點在於Recycler
的recycleViewHolderInternal
方法裏面。我將mCacheViews
數組的回收路徑大概分爲三類,咱們來看看:
- 在從新佈局回收了。這種狀況主要出如今調用了
Adapter
的notifyDataSetChange
方法,而且此時Adapter
的hasStableIds
方法返回爲false。從這裏看出來,爲何notifyDataSetChange
方法效率爲何那麼低,同時也知道了爲何重寫hasStableIds
方法能夠提升效率。由於notifyDataSetChange
方法使得RecyclerView
將回收的ViewHolder
放在二級緩存,效率天然比較低。- 在複用時,從一級緩存裏面獲取到
ViewHolder
,可是此時這個ViewHolder
已經不符合一級緩存的特色了(好比Position失效了,跟ViewType對不齊),就會從一級緩存裏面移除這個ViewHolder
,從添加到mCacheViews
裏面- 當調用
removeAnimatingView
方法時,若是當前ViewHolder
被標記爲remove,會調用recycleViewHolderInternal
方法來回收對應的ViewHolder
。調用removeAnimatingView
方法的時機表示當前的ItemAnimator
已經作完了。
一個ViewHolder
回收到mHiddenView
數組裏面的條件比較簡單,若是當前操做支持動畫,就會調用到RecyclerView
的addAnimatingView
方法,在這個方法裏面會將作動畫的那個View
添加到mHiddenView
數組裏面去。一般就是動畫期間能夠會進行復用,由於mHiddenViews
只在動畫期間纔會有元素。
RecyclerViewPool
跟mCacheViews
,都是經過recycleViewHolderInternal
方法來進行回收,因此情景與mCacheViews
差很少,只不過當不知足放入mCacheViews
時,纔會放入到RecyclerViewPool
裏面去。
瞭解了RecyclerView
的複用和回收機制以後,這個問題就變得很簡單了。我從兩個方面來解釋緣由。
咱們先來看看複用怎麼能體現hasStableIds
能提升效率呢?來看看代碼:
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
if (holder != null) {
// update position
holder.mPosition = offsetPosition;
fromScrapOrHiddenOrCache = true;
}
}
複製代碼
在前面經過Position
方式來獲取一個ViewHolder
失敗以後,若是Adapter
的hasStableIds
方法返回爲true,在進行經過ViewType
方式來獲取ViewHolder
時,會優先到1級或者二級緩存裏面去尋找,而不是直接去RecyclerViewPool
裏面去尋找。從這裏,咱們能夠看到,在複用方面,hasStableIds
方法提升了效率。
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
if (viewHolder.shouldIgnore()) {
if (DEBUG) {
Log.d(TAG, "ignoring view " + viewHolder);
}
return;
}
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
複製代碼
從上面的代碼中,咱們能夠看出,若是hasStableIds
方法返回爲true的話,這裏全部的回收都進入scrap
數組裏面。這恰好與前面對應了。
經過如上兩點,咱們就能很好的理解爲何hasStableIds
方法返回true會提升效率。
RecyclerView
回收和複用機制到這裏分析的差很少了。這裏作一個小小的總結。
- 在
RecyclerView
內部有4級緩存,每一級的緩存所表明的意思都不同,同時複用的優先也是從上到下,各自的回收也是不同。mHideenViews
的存在是爲了解決在動畫期間進行復用的問題。ViewHolder
內部有不少的flag,在理解回收和複用機制以前,最好是將ViewHolder
的flag梳理清楚。
最後用一張圖片來結束本文的介紹。
若是不出意外的話,下一篇文章應該是分析RecyclerView
的動畫機制。