RecyclerView(二)

2021-10-14  本文已影响0人  涛涛123759

Android知识总结

一、缓存

缓存流程图

二、缓存 mCachedViews

2.1、第一种进入缓存方式

执行LinearLayoutManager.onLayoutChildren方法

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    ...
    detachAndScrapAttachedViews(recycler);
    ...
}
public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
    final int childCount = getChildCount();
    for (int i = childCount - 1; i >= 0; i--) {
        final View v = getChildAt(i);
        scrapOrRecycleView(recycler, i, v);
    }
}
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()) {
        //将index的view从mHiddenViews移除的动作
        removeViewAt(index);
        //向 mCachedViews 或 Pool 中添加
        recycler.recycleViewHolderInternal(viewHolder);
    } else {
        //设置RecyclerView这个位置的view的parent为null, 并标记ViewHolder为FLAG_TMP_DETACHED
        detachViewAt(index);
        //添加到mAttachedScrap 或 mCachedViews集合中
        recycler.scrapView(view);
        mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
    }
}

2.2、第二种进入缓存方式

进入RecyclerView内部类Recycler#scrapView

--》 fill 
--》 recycleByLayoutState
--》 recycleViewsFromStart
--》 recycleChildren
--》 removeAndRecycleViewAt
--》 recycler.recycleView
--》 recycleViewHolderInternal // 关键代码,(cacheView、pool)

我们直接以LinearLayoutManager#recycleViewsFromStart开始

private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
    if (!layoutState.mRecycle || layoutState.mInfinite) {
        return;
    }
    int scrollingOffset = layoutState.mScrollingOffset;
    int noRecycleSpace = layoutState.mNoRecycleSpace;
    if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
        recycleViewsFromEnd(recycler, scrollingOffset, noRecycleSpace);
    } else {
        recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace);
    }
}
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,
        int noRecycleSpace) {
    if (scrollingOffset < 0) {
        if (DEBUG) {
            Log.d(TAG, "Called recycle from start with a negative value. This might happen"
                    + " during layout changes but may be sign of a bug");
        }
        return;
    }
    // ignore padding, ViewGroup may not clip children.
    final int limit = scrollingOffset - noRecycleSpace;
    final int childCount = getChildCount();
    if (mShouldReverseLayout) {
        for (int i = childCount - 1; i >= 0; i--) {
            View child = getChildAt(i);
            if (mOrientationHelper.getDecoratedEnd(child) > limit
                    || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                // stop here
                recycleChildren(recycler, childCount - 1, i);
                return;
            }
        }
    } else {
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (mOrientationHelper.getDecoratedEnd(child) > limit
                    || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                // stop here
                recycleChildren(recycler, 0, i);
                return;
            }
        }
    }
}
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
    if (startIndex == endIndex) {
        return;
    }
    if (DEBUG) {
        Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
    }
    if (endIndex > startIndex) {
        for (int i = endIndex - 1; i >= startIndex; i--) {
            removeAndRecycleViewAt(i, recycler);
        }
    } else {
        for (int i = startIndex; i > endIndex; i--) {
            removeAndRecycleViewAt(i, recycler);
        }
    }
}

执行RecyclerView内部类LayoutManager#removeAndRecycleViewAt

public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
    final View view = getChildAt(index);
    removeViewAt(index);
    recycler.recycleView(view);
}

执行RecyclerView内部类Recycler#recycleView

public void recycleView(@NonNull View view) {
    ViewHolder holder = getChildViewHolderInt(view);
    if (holder.isTmpDetached()) {
        removeDetachedView(view, false);
    }
    if (holder.isScrap()) {
        holder.unScrap();
    } else if (holder.wasReturnedFromScrap()) {
        holder.clearReturnedFromScrapFlag();
    }
    //向 mCachedViews 或 Pool 中添加
    recycleViewHolderInternal(holder);
    if (mItemAnimator != null && !holder.isRecyclable()) {
        mItemAnimator.endAnimation(holder);
    }
}

2.1、 mCachedViews 或 Pool 缓存

RecyclerView的集合中缓存

void recycleViewHolderInternal(RecyclerView.ViewHolder holder) {
    final boolean transientStatePreventsRecycling = holder
            .doesTransientStatePreventRecycling();
    @SuppressWarnings("unchecked")
    final boolean forceRecycle = mAdapter != null
            && transientStatePreventsRecycling
            && mAdapter.onFailedToRecycleView(holder);
    boolean cached = false;
    boolean recycled = false;
    if (DEBUG && mCachedViews.contains(holder)) {
        throw new IllegalArgumentException("cached view received recycle internal? "
                + holder + exceptionLabel());
    }
    if (forceRecycle || holder.isRecyclable()) {
        if (mViewCacheMax > 0
                && !holder.hasAnyOfTheFlags(RecyclerView.ViewHolder.FLAG_INVALID
                | RecyclerView.ViewHolder.FLAG_REMOVED
                | RecyclerView.ViewHolder.FLAG_UPDATE
                | RecyclerView.ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
            // Retire oldest cached view
            int cachedViewSize = mCachedViews.size();
            //大于缓存的最大值 mViewCacheMax = 2, CacheView 把老的移除集合,放入 pool 中
            if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                //取出集合的第一个元素放入 pool 中
                recycleCachedViewAt(0);
                //缓存的个数减一
                cachedViewSize--;
            }

            int targetCacheIndex = cachedViewSize;
            if (ALLOW_THREAD_GAP_WORK
                    && cachedViewSize > 0
                    && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
                // when adding the view, skip past most recently prefetched views
                int cacheIndex = cachedViewSize - 1;
                while (cacheIndex >= 0) {
                    int cachedPos = mCachedViews.get(cacheIndex).mPosition;
                    if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
                        break;
                    }
                    cacheIndex--;
                }
                targetCacheIndex = cacheIndex + 1;
            }
            //加入 mCachedViews 集合
            mCachedViews.add(targetCacheIndex, holder);
            cached = true;
        }
        if (!cached) {
            // 添加到Pool中
            addViewHolderToRecycledViewPool(holder, true);
            recycled = true;
        }
    } else {
        if (DEBUG) {
            Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
                    + "re-visit here. We are still removing it from animation lists"
                    + exceptionLabel());
        }
    }
    mViewInfoStore.removeViewHolder(holder);
    if (!cached && !recycled && transientStatePreventsRecycling) {
        holder.mOwnerRecyclerView = null;
    }
}
void addViewHolderToRecycledViewPool(@NonNull RecyclerView.ViewHolder holder, boolean dispatchRecycled) {
    clearNestedRecyclerViewIfNotNested(holder);
    View itemView = holder.itemView;
    if (mAccessibilityDelegate != null) {
        AccessibilityDelegateCompat itemDelegate = mAccessibilityDelegate.getItemDelegate();
        AccessibilityDelegateCompat originalDelegate = null;
        if (itemDelegate instanceof RecyclerViewAccessibilityDelegate.ItemDelegate) {
            originalDelegate =
                    ((RecyclerViewAccessibilityDelegate.ItemDelegate) itemDelegate)
                            .getAndRemoveOriginalDelegateForItem(itemView);
        }
        // Set the a11y delegate back to whatever the original delegate was.
        ViewCompat.setAccessibilityDelegate(itemView, originalDelegate);
    }
    if (dispatchRecycled) {
        dispatchViewRecycled(holder);
    }
    holder.mOwnerRecyclerView = null;
    getRecycledViewPool().putRecycledView(holder);
}
public void putRecycledView(RecyclerView.ViewHolder scrap) {
    final int viewType = scrap.getItemViewType();
    final ArrayList<RecyclerView.ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
    if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
        return;
    }
    if (DEBUG && scrapHeap.contains(scrap)) {
        throw new IllegalArgumentException("this scrap item already exists");
    }
        //进行漂白,即清除一些标志信息个数据
    scrap.resetInternal();
        //保存 ViewHolder
    scrapHeap.add(scrap);
}

调用RecyclerView.RecycledViewPool中的方法getScrapDataForType

private ScrapData getScrapDataForType(int viewType) {
    ScrapData scrapData = mScrap.get(viewType);
    if (scrapData == null) {
        scrapData = new ScrapData();
        mScrap.put(viewType, scrapData);
    }
    return scrapData;
}
static class ScrapData {
    @UnsupportedAppUsage
        //pool 的实际缓存对象,一种 ViewType 对应一个 ScrapData 
    ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
    int mMaxScrap = DEFAULT_MAX_SCRAP;
    long mCreateRunningAverageNs = 0;
    long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
void resetInternal() {
    mFlags = 0;
    mPosition = NO_POSITION;
    mOldPosition = NO_POSITION;
    mItemId = NO_ID;
    mPreLayoutPosition = NO_POSITION;
    mIsRecyclableCount = 0;
    mShadowedHolder = null;
    mShadowingHolder = null;
    clearPayload();
    mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
    mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
    clearNestedRecyclerViewIfNotNested(this);
}

2.2、mAttachedScrap 或 mCachedViews 缓存

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);
    }
}

二、总结

  • 1、从缓存池(RecycledViewPool)复用 ViewHolder 需要调用 onBindViewHodler。

  • 2、从屏幕外缓存(mCachedViews)复用 ViewHolder 不需要调用 onBindViewHodler。

  • 3、从 mCachedViews 中没有拿到 ViewHolder,需要调用 onCreateViewHolder 和 onBindViewHodler。

上一篇下一篇

猜你喜欢

热点阅读