RecyclerView的緩存真是4級嗎?

網(wǎng)上的大多數(shù)博客都是認為RecyclerView是4級緩存,但真的是4級緩存嗎?知道我看了源碼,才發(fā)現(xiàn),RecyclerView做的遠遠不止4級緩存,當然是根據(jù)LayoutManager不同,緩存實現(xiàn)也不同,本文主要分析最最常用的LinearLayoutManager。

寫的有點亂,以后整理吧

RecyclerView復用過程

RecyclerView的中的緩存復用是由內部類Recycler來維護的,在RecyclerView.Adapter調用onCreateViewHolder來創(chuàng)建ViewHolder,這時就開始了利用緩存機制;

這里是以LinearLayoutManager為例,其他也差不多,只是一些細節(jié)不一樣;
先看下調用流程吧,否則直接看緩存,也不知道什么時候調用的:


RecyclerView.png

可以看到,RecyclerView的在onMeasure時,就將item所有的操作都交給了LayoutManager,并在創(chuàng)建ViewHolder時,將緩存復用機制交給了RecyclerView內部類Recycler,Recycler的tryGetViewHolderForPositionByDeadline方法內,實現(xiàn)了所有的復用機制;

緩存實現(xiàn)

Recycler :緩存機制的管理類

Recycler.png
public final class Recycler {
        //主要由void scrapView(View view)來處理, 仍舊綁定在RecyclerView上,但是能rebinding和reuse;
        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
        ArrayList<ViewHolder> mChangedScrap = null;
        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
        private final List<ViewHolder> mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
        RecycledViewPool mRecyclerPool;
        private ViewCacheExtension mViewCacheExtension;
 }

從代碼里這來看,里面就有4個集合外加一個RecycledViewPool和一個ViewCacheExtension,所以遠不止4級緩存;

緩存的實現(xiàn)

接下來我們詳細看下復用機制的實現(xiàn):
其實在LayoutState的next方法中就有一層復用,mScrapList實際就是mUnmodifiableAttachedScrap,這個調用是在onLayoutChildren最后的layoutForPredictiveAnimations的方法中,做動畫時,直接去拿mUnmodifiableAttachedScrap中的itemView;
否則才是真正的去創(chuàng)建View;

View next(RecyclerView.Recycler recycler) {
    if (mScrapList != null) {
        return nextViewFromScrapList();
    }
    final View view = recycler.getViewForPosition(mCurrentPosition);
    mCurrentPosition += mItemDirection;
    return view;

getViewForPosition中主要調用tryGetViewHolderForPositionByDeadline方法:

ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {            
            RecyclerView.ViewHolder holder = null;
            // 0) If there is a changed scrap, try to find from there
            // isPreLayout的條件是在RecyclerView的onMeasure中,如果不是自動測量,adapter大小不是固定的,或者是自定義onMeasure
            //如果只是changed,就會進入到getChangedScrapViewForPosition,里面主要從mChangedScrap取數(shù)據(jù);
            if (mState.isPreLayout()) {
                //里面主要是從mChangedScrap中找
                holder = getChangedScrapViewForPosition(position);
            }
            // 1) Find by position from scrap/hidden list/cache
            if (holder == null) {
                // 里面主要從mAttachedScrap和ChildHelper的mHiddenViews還有mCachedViews中查找
                //這里面是確切的匹配,里面各種狀態(tài)必須完全一致,拿出來的,不需要經過rebinding
                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
            }
            if (holder == null) {
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                final int type = mAdapter.getItemViewType(offsetPosition);
                // 2) Find from scrap/cache via stable ids, if exists
                //hasStableIds默認是true,在adapter的構造中賦值的
                if (mAdapter.hasStableIds()) {
                    //利用Position找不到時,再用id和type匹配,拿出來之后需要rebinding
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
                }
                //  用戶自定義擴展緩存
                if (holder == null && mViewCacheExtension != null) {
                    // We are NOT sending the offsetPosition because LayoutManager does not
                    // know it.
                    final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                    }
                }
                //RecycledView復用池
                if (holder == null) { // fallback to pool
                    holder = getRecycledViewPool().getRecycledView(type);
                }
                //  創(chuàng)建新的Item
                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);
                        }
                    }
                 }
            }
            return holder;
}

在LayoutState中的next方法中還有一層緩存,利用的mScrapList ,其實mScrapList 就是mUnmodifiableAttachedScrap

View next(RecyclerView.Recycler recycler) {
            if (mScrapList != null) {
                return nextViewFromScrapList();
            }
            final View view = recycler.getViewForPosition(mCurrentPosition);
            mCurrentPosition += mItemDirection;
            return view;
}
private View nextViewFromScrapList() {
            final int size = mScrapList.size();
            for (int i = 0; i < size; i++) {
                final View view = mScrapList.get(i).itemView;
                final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
                if (lp.isItemRemoved()) {
                    continue;
                }
                if (mCurrentPosition == lp.getViewLayoutPosition()) {
                    assignPositionFromScrapList(view);
                    return view;
                }
            }
            return null;
}

這里面涉及到2個緩存mAttachedScrap和mChangedScrap,可以看到這2個緩存的定義了

        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(...);
                }
                holder.setScrapContainer(this, false);
                mAttachedScrap.add(holder);
            } else {
                if (mChangedScrap == null) {
                    mChangedScrap = new ArrayList<ViewHolder>();
                }
                holder.setScrapContainer(this, true);
                mChangedScrap.add(holder);
            }
        }

總結

寫的有些亂,代碼太多了,都很重要,建議自己再看一遍


緩存機制.png
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容