天天看點

RecyclerView源碼分析之四級緩存機制

衆所周知,RecyclerView在android中實作清單是性能非常好的,那麼性能好的原因在哪裡呢?關鍵還是在它在處理view時的回收和複用。清單在滑動的時候,會進行itemView的回收和複用,那麼我們就從滑動回調即onTouchEvent來入手分析。

基本概念

  1. ViewHolder: View的容器,一項View就對應一個ViewHolder
  2. Recyler:RecyclerView的内部類,主要負責View的回收和複用
  3. LinearLayoutManager: RecyclerView的線性布局管理器

滑動時函數調用鍊

RecyclerView源碼分析之四級緩存機制

四級緩存機制

層級 緩存變量 緩存名 容量 資料結構 緩存用途
1 mChangeScrap與 mAttachedScrap 可見緩存 x ArrayList 用于布局過程中螢幕可見表項的回收和複用
2 mCachedViews 緩存清單 2 ArrayList 用于移出螢幕表項的回收和複用,不會清空資料
3 mViewCacheExtension 自定義緩存 x
4 RecycledViewPool 緩存池 5 SparseArray 用于移出螢幕表項的回收和複用,會将ViewHolder的資料重置

回收的關鍵方法分析

## RecyclerView.java
void recycleViewHolderInternal(ViewHolder holder) {
	...
	if (forceRecycle || holder.isRecyclable()) {
                if (mViewCacheMax > 0
                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
                                | ViewHolder.FLAG_REMOVED
                                | ViewHolder.FLAG_UPDATE
                                | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) 
	// 1) 先嘗試放到cacheView中
                    int cachedViewSize = mCachedViews.size();
                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                        // 如果 mCachedViews 已經滿了,把第0個位置的移除并放到 緩存池中
                        recycleCachedViewAt(0);
                        cachedViewSize--;
                    }
                    if (!cached) {
                    // 2) 如果CacheView中沒放進去,就放到 緩存池中
                    addViewHolderToRecycledViewPool(holder, true);
                    recycled = true;
                }
           ...
}
                                
}
           

複用的關鍵方法分析

tryGetViewHolderForPositionByDeadline

  1. 從一級緩存 mChangeScrap 中取
  2. 從二級緩存 mCachedViews中取
  3. 從三級緩存 mViewCacheExtension 中取
  4. 從四級緩存 緩存池中取
  5. 緩存中都沒有拿到值,就直接建立
  6. 未綁定過時,進行綁定
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
                boolean dryRun, long deadlineNs) {
	ViewHolder holder = null;
            // 1) 從一級緩存 changed scrap 中取
            if (mState.isPreLayout()) {
                holder = getChangedScrapViewForPosition(position);
                fromScrapOrHiddenOrCache = holder != null;
            }
            // 2)從二級緩存 cache中取
            if (holder == null) {
				final int type = mAdapter.getItemViewType(offsetPosition);
                if (mAdapter.hasStableIds()) {
                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                            type, dryRun);
                    if (holder != null) {
                        // update position
                        holder.mPosition = offsetPosition;
                        fromScrapOrHiddenOrCache = true;
                    }
                }
                // 3. 從三級緩存 CacheExtension 中取  
                if (holder == null && mViewCacheExtension != null) {
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    if (view != null) {
                        holder = getChildViewHolder(view);
                    }
                }
                // 4) 從四級緩存 緩存池中取
                if (holder == null) { // fallback to pool
                    holder = getRecycledViewPool().getRecycledView(type);
                    if (holder != null) {
                        holder.resetInternal();
                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
                            invalidateDisplayListInt(holder);
                        }
                    }
                }
                // 5)緩存中都沒有拿到值,就直接建立
				if (holder == 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");
                    }
                }
			}
			// 6)已經 bind過了,不會再去綁定,未綁定過時,進行綁定
			if (mState.isPreLayout() && holder.isBound()) {
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                //  嘗試 bindView
                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
            }
            return holder;
}
           

一級緩存-緩存碎片

ViewHolder getChangedScrapViewForPosition(int position) {
            // If pre-layout, check the changed scrap for an exact match.
            final int changedScrapSize;
            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
                return null;
            }
            // find by position
            for (int i = 0; i < changedScrapSize; i++) {
                final ViewHolder holder = mChangedScrap.get(i);
                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                    return holder;
                }
            }
            // find by id
            if (mAdapter.hasStableIds()) {
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
                    final long id = mAdapter.getItemId(offsetPosition);
                    for (int i = 0; i < changedScrapSize; i++) {
                        final ViewHolder holder = mChangedScrap.get(i);
                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                            return holder;
                        }
                    }
                }
            }
            return null;
}
           

二級緩存-緩存清單

ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
			// 1) 先從mAttachedScrap中取,取到便傳回
            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()) {
                    if (type == holder.getItemViewType()) {
                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                        if (holder.isRemoved()) {
                            if (!mState.isPreLayout()) {
                                holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
                                        | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
                            }
                        }
                        return holder;
                    } else if (!dryRun) {
                        mAttachedScrap.remove(i);
                        removeDetachedView(holder.itemView, false);
                        quickRecycleScrapView(holder.itemView);
                    }
                }
            }

            // 2)二級緩存,從mCachedViews中取
            final int cacheSize = mCachedViews.size();
            for (int i = cacheSize - 1; i >= 0; i--) {
                final ViewHolder holder = mCachedViews.get(i);
                //從mCachedViews中取到後便傳回
                if (holder.getItemId() == id) {
                    if (type == holder.getItemViewType()) {
                        if (!dryRun) {
                            mCachedViews.remove(i);
                        }
                        return holder;
                    } else if (!dryRun) {
                        recycleCachedViewAt(i);
                        return null;
                    }
                }
            }
            return null;
        }
           

三級緩存-自定義緩存

四級緩存-緩存池

public ViewHolder getRecycledView(int viewType) {
			//從mScrap中根據view的類型來取出一個
            final ScrapData scrapData = mScrap.get(viewType);
            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
                //從 scrapData 中拿最後一個資料,先進後出
                return scrapHeap.remove(scrapHeap.size() - 1);
            }
            return null;
}

static class ScrapData {
			//ViewHolder是作為一個List被存進來的
            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
            // 緩存池中 list的大小是5,也就是每個類型的view緩存池中存儲5個
            int mMaxScrap = 5;
            long mCreateRunningAverageNs = 0;
            long mBindRunningAverageNs = 0;
 }