RecyclerView之緩存設計

前言

RecyclerView,見名知義,這個View代表了可循環使用的視圖集合控件,封裝了View的緩存邏輯判斷,RecyclerView的基本單元是ViewHolder,裏面有一個itemView代表了視圖上的子View,所以RecyclerView的緩存基本單元也是ViewHolder。本文將從源碼的角度來講解RecyclerView的緩存設計。

本文相關源碼基於Android8.0,相關源碼位置如下:
frameworks/support/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
frameworks/support/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java

Recycler介紹

這裏首先介紹一下Recycler,它定義在RecyclerView中,如下:

public final class Recycler {
    final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();//緩存着在屏幕中顯示的ViewHolder
    final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();//緩存着已經滾動出屏幕的ViewHolder,即屏幕外的ViewHolder
    RecycledViewPool mRecyclerPool;//ViewHolder的緩存池,屏幕外緩存的mCachedViews已滿時,會將ViewHolder緩存到RecycledViewPool中。
    private ViewCacheExtension mViewCacheExtension;//自定義緩存,自己實現ViewCacheExtension類來實現緩存。
    ArrayList<ViewHolder> mChangedScrap = null;//屏幕內緩存,緩存着數據已經改變的ViewHolder
    int mViewCacheMax = DEFAULT_CACHE_SIZE;//mCachedViews默認緩存數量
    static final int DEFAULT_CACHE_SIZE = 2;//默認緩存數量爲2
    private int mRequestedCacheMax = DEFAULT_CACHE_SIZE; //可以設置mCachedViews的最大緩存數量,默認爲2
    //...
}

Recycler是RecyclerView的核心類,是RecyclerView的緩存實現類,它有着四級緩存:

  • 1、mAttachedScrap
    屏幕內緩存, 當我們調用notifiXX函數重新佈局時,在佈局之前,LayoutManager會調用detachAndScrapAttachedViews(recycler)把在RecyclerView中顯示的ViewHolder一個個的剝離下來,然後緩存在mAttachedScrap中,等佈局時會先從mAttachedScrap查找,再把ViewHolder一個個的放回RecyclerView原位中去,mAttachedScrap只是單純的保存從RecyclerView中剝離的ViewHolder,再重新放回RecyclerView中去,如果放回後還有剩餘的ViewHolder沒有參加新佈局,會從mAttachedScrap移到mCachedViews中。

  • 2、mCachedViews
    屏幕外緩存,在RecyclerView滾動時,對於那些不在RecyclerView中顯示的ViewHolder,LayoutManager會調用removeAndRecycleAllViews(recycler)把這些已經移除的ViewHolder緩存在mCacheViews中,它的默認大小是2,當它滿了的時候,就會利用先進先出原則,把老的ViewHolder移到mRecyclerPool中,mCachedViews它只是緩存最新被移除出屏幕的ViewHolder。

  • 3、mViewCacheExtension
    自定義緩存實現,一般而言,我們不會自定義緩存實現,使用Recycler提供的3級緩存足夠。

  • 4、mRecyclerPool
    緩存池,通過前面1、2可以知道,真正廢棄的ViewHolder最終移到mRecyclerPool,當我們向RecyclerView申請一個HolderView來使用的時,如果在mAttachedScrap、mCachedViews匹配不到,即使他們中有ViewHolder也不會返回給我們使用,而是會到mRecyclerPool中去拿一個廢棄的ViewHolder返回。

    mRecyclerPool內部維護了一個SparseArray,在mRecyclerPool中會根據每個ViewType把ViewHolder分別存儲在不同的列表中,每個ViewType默認緩存5個ViewHolder,而且RecyclerViewPool也可以是多個RecyclerView之間的ViewHolder的緩存池,只要通過RecyclerView.setRecycledViewPool(RecycledViewPool)設置同一個RecycledViewPool,設置時,不需要自己去new 一個 RecyclerViewPool,每個RecyclerView默認都有一個RecyclerViewPool,只需要通過mRecyclerView.getRecycledViewPool()獲取。RecyclerViewPool大概結構如下:

public static class RecycledViewPool {
    private static final int DEFAULT_MAX_SCRAP = 5;
    
    static class ScrapData {
        final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
        int mMaxScrap = DEFAULT_MAX_SCRAP;
        //...
    }
    
    //SparseArray的key爲type,value爲ScrapData,ScrapData中包含ViewHolder列表
    SparseArray<ScrapData> mScrap = new SparseArray<>();
    
    //...
    
    //根據type從緩存池中獲取一個ViewHolder
    public ViewHolder getRecycledView(int viewType) {
        final ScrapData scrapData = mScrap.get(viewType);
        if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
            return scrapHeap.remove(scrapHeap.size() - 1);
        }
        return null;
    }

    //把一個ViewHolder放入緩存池中緩存
    public void putRecycledView(ViewHolder scrap) {
        final int viewType = scrap.getItemViewType();
        final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
        if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
            return;
        }
        //...
        scrap.resetInternal();
        scrapHeap.add(scrap);
    }
}

所以我們從Recycler中獲取一個ViewHolder時,是這樣的順序:mAttachedScrap -> mCachedViews -> mViewCacheExtension -> mRecyclerPool,當上述步驟都找不到了,就會調用Adapter的creat函數創建一個ViewHolder。那這裏爲什麼省略mChangedScrap不講呢?因爲mChangedScrap是跟RecyclerView的預佈局有關,緩存着RecyclerView中數據改變過的ViewHolder,而預佈局默認爲false,一般是RecyclerView執行動畫時纔會爲true,我們上一篇文章也沒有討論執行動畫的時候的佈局過程,所以這裏就不分析mChangedScrap。

Recycler.getViewForPosition()

在上篇文章中,提到在layoutChunk函數中,首先會調用LayoutState對象的next函數獲取到一個itemView,然後佈局這個itemView,我們來看LayoutState的next函數相關實現:

View next(RecyclerView.Recycler recycler) {
    //省略了一個mScrapList,屬於LayoutManager,跟執行動畫時的緩存有關,這裏不分析
    //...
    //這裏纔是核心,調用Recycler中的getViewForPosition獲取itemView
    final View view = recycler.getViewForPosition(mCurrentPosition);
    //把itemView索引移到下一個位置
    mCurrentPosition += mItemDirection;
    return view;
}

上述代碼實際是調用RecyclerView.Recycler對象的getViewForPosition方法獲取itemView,而該函數最終會獲取一個ViewHolder,從而返回ViewHolder中的itemView,我們來看該函數相關調用和實現:

public View getViewForPosition(int position) {
    return getViewForPosition(position, false);
}

View getViewForPosition(int position, boolean dryRun) {
    //可以看到最終返回的是ViewHolder中的itemView
     return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}

//獲取一個ViewHolder
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    //...
}

Recycler的getViewForPosition方法最終會調用到tryGetViewHolderForPositionByDeadline方法,tryGetViewHolderForPositionByDeadline方法的意圖是通過給定的position從Recycler的scrap, cache,RecycledViewPool獲取一個ViewHolder或者通過Adapter直接創建一個ViewHolder。我們來看tryGetViewHolderForPositionByDeadline方法相關源碼:

//參數解釋:
//position:要獲得哪個位置的ViewHolder
//dryRun: 代表position的ViewHolder是否已經從scrap或cache列表中移除,這裏爲false,表示沒有,因爲佈局函數layoutChildren中一定會調用detachAndScrapAttachedViews(recycler)函數,表示把ViewHolder放入scrap列表中
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    //省略了跟預佈局有關的mChangedScrap獲取ViewHolder,mChangedScrap不屬於常規緩存
    //...
   
    ViewHolder holder = null;
    
    if (holder == null) {
    
        //1、第一次查找,通過position從scrap或hidden或cache中找ViewHolder
         holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
            //如果找到ViewHolder,檢查ViewHolder的合法性
            if (holder != null) {
                //檢查ViewHolder的是否被移除,position是否越界等,如果檢查通過返回true,失敗返回false
                if (!validateViewHolderForOffsetPosition(holder)) {
                    //檢查不通過
                    //上述講過dryRun爲false
                    if (!dryRun) {
                        //設置這個ViewHolder爲無效標誌
                        holder.addFlags(ViewHolder.FLAG_INVALID);
                        //把這個ViewHolder從scrap列表中移除
                        if (holder.isScrap()) {
                            removeDetachedView(holder.itemView, false);
                            holder.unScrap();
                        } 
                        //...
                        //把這個ViewHolder放入cache列表中或mRecyclerPool中
                        recycleViewHolderInternal(holder);
                    }
                    //置空不匹配的ViewHolder,進入下一步查找
                    holder = null;
                } else {
                    //檢查通過了
                    fromScrapOrHiddenOrCache = true;
                }
            }
            
    }
    
     if (holder == null) {
     
        //...
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        //這裏可以看到我們熟悉的Adapter中的getItemViewType方法,重寫此方法可以讓RecyclerView顯示多種type的itemView
        final int type = mAdapter.getItemViewType(offsetPosition);
        
        //如果mAdapter.hasStableIds()爲true,就進入第2次查找,默認返回false
         if (mAdapter.hasStableIds()) {
            //2、第2次查找,根據ViewHolder的type和id從scrap或cached列表查找
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                    type, dryRun);
            if (holder != null) { //找到了
                //更新ViewHolder的位置
                holder.mPosition = offsetPosition;
                fromScrapOrHiddenOrCache = true;
            }
        }
        
        if (holder == null && mViewCacheExtension != null) {
            //3、第3次查找,從自定義緩存中查找,一般我們不會重寫ViewCacheExtension
            final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
            //...
        }
        
         if (holder == null) {
            //4、第4次查找,從RecycledViewPool中查找,可以看到這裏會根據type返回一個使用過的ViewHolder給你
            holder = getRecycledViewPool().getRecycledView(type);
            if (holder != null) {//找到了
                //重置ViewHolder中的信息
                holder.resetInternal();
                //...
            }
        }
        
        //前面的4次還找不到合適的ViewHolder,就重新創建一個
        if (holder == null) {
             //...
             //5、這裏會調用Adapter中的OnCreateViewHolder方法
             holder = mAdapter.createViewHolder(RecyclerView.this, type);
        }
    
    }
    
    //...
    boolean bound = false;
    //6、只要滿足以下3個情況:
    //1、ViewHolder沒有被綁定過,即沒有設置FLAG_BOUND標誌位
    //2、ViewHolder需要更新,即設置了FLAG_UPDATE標誌位
    //3、ViewHolder是無效的,即設置了FLAG_INVALID標誌位
    //就會調用Adapter中的OnBindViewHolder方法
    if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        //這裏最終調用Adapter中的OnBindViewHolder方法
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
    }
    
}

看起來函數很長但是步驟還是很清晰的,我們把它分爲註釋1、2、3、4、5、6來看:

1、調用getScrapOrHiddenOrCachedHolderForPosition()

註釋1中通過position從scrap或hidden或cache中找ViewHolder,我們來看getScrapOrHiddenOrCachedHolderForPosition方法的關鍵源碼:

 ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
 
    //1.1、第一次嘗試,從mAttachedScrap找到一個精確,沒有失效的ViewHolder並返回
    final int scrapCount = mAttachedScrap.size();
    for (int i = 0; i < scrapCount; i++) {
        final ViewHolder holder = mAttachedScrap.get(i);
        if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())){
            //標誌這個ViewHolder是從mAttachedScrap取出並返回的
            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
            return holder;
        }
    }
    
    //1.2、第二次嘗試,dryRun爲false,從RecyclerView中隱藏的itemView中找,如果找到合適的View,就讓它顯示並把它從RecyclerView中剝離,然後根據這個View的LayoutParam獲取ViewHolder,最後把這個ViewHolder放入mAttachedScrap並返回
    if (!dryRun) {
        View view = mChildHelper.findHiddenNonRemovedView(position);
        if (view != null) {
            //獲取ViewHolder
            final ViewHolder vh = getChildViewHolderInt(view);
            //顯示這個View
            mChildHelper.unhide(view);
            //從RecyclerView剝離這個View
            mChildHelper.detachViewFromParent(layoutIndex);
            //把這個ViewHolder放入mAttachedScrap
            scrapView(view);
            vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
                    | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
            //返回
            return vh;
        }
    }
    
    //1.3、第三次嘗試,從mCachedViews找到沒有失效的ViewHolder並返回
    final int cacheSize = mCachedViews.size();
    for (int i = 0; i < cacheSize; i++) {
        final ViewHolder holder = mCachedViews.get(i);
        if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
            if (!dryRun) {
                mCachedViews.remove(i);
            }
            return holder;
        }
    }
    return null;
}

可以看到註釋1的第一次查找,裏面分爲3步:

  • 1.1、從mAttachedScrap找。
  • 1.2、如果上一步沒有得到合適的緩存,從HiddenViews找。
  • 1.3、如果上一步沒有得到合適的緩存,從mCachedViews找。

從上面3個步驟之一找到,就返回ViewHolder,然後檢查ViewHolder的有效性,如果無效,則從mAttachedScrap中移除,並加入到mCacheViews或者mRecyclerPool中,並且將ViewHolder置爲null,走到下一步。

2、調用getScrapOrCachedViewForId()

下一步就是註釋2,如果我們通過Adapter.setHasStableIds(boolean)設置爲true,就會進入,裏面根據ViewHolder的type和id從scrap或cached列表查找ViewHolder,我們來看一下相關源碼該方法的相關源碼:

ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {

//2.1、第一次嘗試,從mAttachedScrap找到一個id相同並且沒有從mAttachedScrap取出並返回過的ViewHolder,還要type相同的ViewHolder返回
    final int count = mAttachedScrap.size();
    for (int i = count - 1; i >= 0; i--) {
        final ViewHolder holder = mAttachedScrap.get(i);
        if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
            //id相同type相同
            if (type == holder.getItemViewType()) {
                holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                //...
                }
                return holder;
            } else if (!dryRun) {
                //id相同但type不同
                //從mAttachedScrap移除這個ViewHolder
                mAttachedScrap.remove(i);
                removeDetachedView(holder.itemView, false);
                //把這個ViewHolder放入caches或RecyclerViewPool
                quickRecycleScrapView(holder.itemView);
            }
        }
    }
    
    //2.2、第2次嘗試,從mCachedViews中找到一個id相同並且type相同的ViewHolder返回
    final int cacheSize = mCachedViews.size();
    for (int i = cacheSize - 1; i >= 0; i--) {
        final ViewHolder holder = mCachedViews.get(i);
        if (holder.getItemId() == id) {
            //id相同並且type相同
            if (type == holder.getItemViewType()) {
                if (!dryRun) {
                    //從cache中移除
                    mCachedViews.remove(i);
                }
                return holder;
            } else if (!dryRun) {
                //id相同type不相同
                //把這個ViewHolder從cache中移除並放入RecyclerViewPool中
                recycleCachedViewAt(i);
                return null;
            }
        }
    }
    return null;
}

可以看到註釋2的第二次查找,裏面分爲2步:

  • 2.1、從mAttachedScrap找。
  • 2.2、如果上一步沒有得到合適的緩存,從mCachedViews找。

第二次查找跟第一次不同的是,它是通過Adapter.getItemId(position)獲得該位置ViewHolder的id,來查找ViewHolder,我們可以重寫Adapter.getItemId(position)返回每個position的ViewHolder的id,默認返回RecyclerView.NO_ID。從上面2個步驟之一找到,就返回ViewHolder,如果找不到就進入下一步。

3、從ViewCacheExtension中找

註釋3的第三次查找是從自定義緩存中查找,這個沒什麼好說,可以直接到下一步。

4、從RecyclerViewRool中找

下一步就是第4次查找,從RecyclerdViewPool中查找,可以看到這裏先使用getRecyclerViewPool獲得Recycler中的RecyclerViewPool,然後調用RecyclerViewPool的getRecycledView(type)根據type獲取一個ViewHolder,我們來看該方法的源碼:

 public ViewHolder getRecycledView(int viewType) {
    //根據type取出ScrapData
    final ScrapData scrapData = mScrap.get(viewType);
    if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
        //取出ScrapData中的ViewHolder列表
        final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
        //返回一個ViewHolder並從pool中刪除
        return scrapHeap.remove(scrapHeap.size() - 1);
    }
    return null;
}

mScrap是SparseArray類型,它會根據type把ViewHolder存放在不同ScrapData中,ScrapData中有一個mScrapHeap,是ArrayList類型,它會存放RecyclerViewPool中放進來的ViewHolder。所以上面這個方法首先會根據type取出ScrapData,然後取出mScrapHeap,如果mScrapHeap有元素,就返回並刪除,然後重置這個ViewHolder讓它複用,如果沒有就進入下一步。

5、調用Adapter的createViewHolder()

既然緩存中沒有就創建一個,該方法的相關源碼如下:

  public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
        //...
        final VH holder = onCreateViewHolder(parent, viewType);
}

可以看到,調用了我們熟悉的onCreateViewHolder方法,該方法就是用來創建ViewHolder。

到這裏,經過tryGetViewHolderForPositionByDeadline方法中的註釋1、2、3、4、5步驟之一拿到了ViewHolder,接下來就是看是否需要調用Adapter的OnBindViewHolder方法綁定ViewHolder。

6、根據情況調用Adapter的OnBindViewHolder()

從上面知道當緩存中不能提供ViewHolder就會調用adapter的onCreateViewHolder創建一個,那麼我們同樣熟悉的OnBindViewHolder方法是什麼時候執行的呢?如下:

ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    //...
    ViewHolder holder = null;
    
    //...
    
    //走到這裏表示holder已經經過各種手段賦值了

    //6、只要滿足以下3個情況:
    //1、ViewHolder沒有被綁定過,即沒有設置FLAG_BOUND標誌位
    //2、ViewHolder需要更新,即設置了FLAG_UPDATE標誌位
    //3、ViewHolder是無效的,即設置了FLAG_INVALID標誌位
    //就會調用Adapter中的OnBindViewHolder方法
    if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        //這裏最終調用Adapter中的OnBindViewHolder方法
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
    }
}


private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition, int position, long deadlineNs) {
    //...
    //調用Adapter的OnBindViewHolder方法
    mAdapter.bindViewHolder(holder, offsetPosition);
    return true;
}

public abstract static class ViewHolder {
    //...
    boolean isBound() {
        return (mFlags & FLAG_BOUND) != 0;
    }

    boolean needsUpdate() {
        return (mFlags & FLAG_UPDATE) != 0;
    }


    boolean isInvalid() {
        return (mFlags & FLAG_INVALID) != 0;
    }
}


holder.isBound()、holder.needsUpdate() 、holder.isInvalid()方法中會分別判斷ViewHolder中有沒有設置FLAG_BOUND標誌位、FLAG_UPDATE標誌位、FLAG_INVALID標誌位,只要滿足3種情況之一,就會調用Adapter的OnBindViewHolder方法綁定數據,這3種情況的解釋如下:

  • 1、沒有設置FLAG_BOUND標誌位:它表示ViewHolder沒有調用過OnBindViewHolder方法,一般是調用Adapter的OnCreateViewHolder方法創建的ViewHolder會出現這種情況;
  • 2、設置了FLAG_UPDATE標誌位:它表示ViewHolder需要更新,一般是調用了Adapter的定向更新的相關方法或者ViewHolder是從RecycledViewPool中取出的就會出現這種情況;
  • 3、設置了FLAG_INVALID標誌位:它表示ViewHolder是無效的,一般是從mAttachedScrap或mCacheViews中取出ViewHolder後,發現它滿足被移除或者position越界了等不合法的條件,就會把取出ViewHolder設置FLAG_INVALID標誌位,標誌無效,然後調用recycleViewHolderInternal方法把它放入mCacheViews或RecycledViewPool中,在recycleViewHolderInternal方法中ViewHolder首先會被嘗試放入mCacheViews(默認大小爲2)中,如果滿了,就會利用先進先出原則,把老的ViewHolder移到mRecyclerPool中。

bind方法是用來綁定數據,對於從mAttachedScrap中拿出來的ViewHolder是不用重新bind的,而對於從mRecyclerPool拿出和通過Create方法創建的ViewHolder是需要重新bind的,而對於從mCacheViews中拿出的ViewHolder有可能會被bind,當調用getScrapOrHiddenOrCachedHolderForPosition方法根據position獲取ViewHolder時,如果這個ViewHoler是從mCacheViews中取出的,說明滿足有效的、positioin匹配這兩種情況,如果這個ViewHolder同時是合法的,那麼這個ViewHolder不需要重新bind,而如果是不合法的,就會標誌無效,再次放入mCacheViews中(有可能會移動到mRecyclerPool),等待調用getScrapOrCachedViewForId方法根據type和id從mCacheViews再次獲取這個已經被標記爲無效的ViewHolder,如果這個無效的ViewHolder的type和id都匹配的話,就會獲取這個無效的ViewHolder,而此時這個ViewHolder是需要重新bind的。

從前面的分析來看,mAttachedScrap和mCacheViews都是position匹配或者type和id匹配纔會命中返回ViewHolder,而mRecyclerPool則沒有這些限制,只要mRecyclerPool中相應type類型的ViewHolder緩存有,就會命中返回ViewHolder,且優先級mAttachedScrap > mCacheViews > mRecyclerPool,通過以下3個場景,加深大家理解mAttachedScrap、mCacheViews、mRecyclerPool的作用:

1、當RecyclerView列表上下滑動時,屏幕內的ViewHolder會被緩存到mAttachedScrap中,在屏幕內改變位置的ViewHolder復位後,很快會從mAttachedScrap複用到原位置上;

2、當RecyclerView列表向上滑動,列表頂部有ViewHolder滑出屏幕,滑出屏幕的ViewHolder會被緩存到mCacheViews中,當列表向下滑動復位時,滑出屏幕的ViewHolder很快從mCacheViews複用到原位置上;

3、當RecyclerView列表向上滑動,列表頂部有ViewHolder滑出屏幕,滑出屏幕的ViewHolder會被緩存到mCacheViews中,多餘的會移動到mRecyclerPool中,列表底部有空餘的ViewHolder位置,這時會從mRecyclerPool取出ViewHolder複用,填充底部空餘的ViewHolder位置。

總結

本文中源碼角度簡單的分析RecyclerView佈局一個itemView時是怎樣通過Recycler來獲取一個ViewHolder,從而獲取itemView,如圖:

準確的來說,Recycler是RecyclerView的itemView的提供者和管理者,它在內部封裝了RecyclerView的緩存設計實現,在RecyclerView中有着四級緩存:AttachedScrap,mCacheViews,ViewCacheExtension,RecycledViewPool,正因爲這樣RecyclerView在使用的時候效率更好。

參考文章:

RecyclerView和ListView原理

RecyclerView緩存機制(咋複用?)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章