RecyclerView缓存分析

来源:互联网 发布:网络代购图片 编辑:程序博客网 时间:2024/06/05 10:21

缓存介绍

public final class Recycler {        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();        ArrayList<ViewHolder> mChangedScrap = null;        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();// mAttachedScrap的不可变视图        private final List<ViewHolder>                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);// 预缓存数,设置后会相应更新mViewCacheMax的值        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;// 最大缓存数        int mViewCacheMax = DEFAULT_CACHE_SIZE;        RecycledViewPool mRecyclerPool;        private ViewCacheExtension mViewCacheExtension;        static final int DEFAULT_CACHE_SIZE = 2;}
RecyclerView拥有三级缓存(算上mAdapter.createViewHolder的话其实就有四级了),我们先看下各个缓存变量的用处。之后会分析RecyclerView是如何使用这些缓存的。

变量作用mAttachedScrap未与RecyclerView分离的ViewHolder列表(即一级缓存)mChangedScrapRecyclerView中需要改变的ViewHolder列表(即一级缓存)mCachedViewsRecyclerView的ViewHolder缓存列表(即一级缓存)mViewCacheExtension用户设置的RecyclerView的ViewHolder缓存列表扩展(即二级缓存)mRecyclerPoolRecyclerView的ViewHolder缓存池(即三级缓存)

获取缓存

对于多级缓存的使用我们来看下tryGetViewHolderForPositionByDeadline方法
ViewHolder tryGetViewHolderForPositionByDeadline(int position,                                                              boolean dryRun, long deadlineNs) {    if (position < 0 || position >= mState.getItemCount()) {        throw new IndexOutOfBoundsException("Invalid item position " + position                + "(" + position + "). Item count:" + mState.getItemCount());    }    boolean fromScrapOrHiddenOrCache = false;    ViewHolder holder = null;    // 0) If there is a changed scrap, try to find from there    if (mState.isPreLayout()) {        // 从mChangedScrap中获取ViewHolder        holder = getChangedScrapViewForPosition(position);        fromScrapOrHiddenOrCache = holder != null;    }    // 1) Find by position from scrap/hidden list/cache    if (holder == null) {        // 从mAttachedScrap 与 mCachedViews中获取ViewHolder        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.                    // FLAG_INVALID 回收标记                    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;            }        }    }    if (holder == null) {        final int offsetPosition = mAdapterHelper.findPositionOffset(position);        if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {            throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "                    + "position " + position + "(offset:" + offsetPosition + ")."                    + "state:" + mState.getItemCount());        }        final int type = mAdapter.getItemViewType(offsetPosition);        // 2) Find from scrap/cache via stable ids, if exists        if (mAdapter.hasStableIds()) {            // 通过id查找ViewHolder            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),                    type, dryRun);            if (holder != null) {                // update position                holder.mPosition = offsetPosition;                fromScrapOrHiddenOrCache = true;            }        }        if (holder == null && mViewCacheExtension != null) {            // We are NOT sending the offsetPosition because LayoutManager does not            // know it.            // 如果使用者定义了mViewCacheExtension,则从中开始查找ViewHolder            final View view = mViewCacheExtension                    .getViewForPositionAndType(this, position, type);            if (view != null) {                holder = getChildViewHolder(view);                if (holder == null) {                    throw new IllegalArgumentException("getViewForPositionAndType returned"                            + " a view which does not have a ViewHolder");                } else if (holder.shouldIgnore()) {                    throw new IllegalArgumentException("getViewForPositionAndType returned"                            + " a view that is ignored. You must call stopIgnoring before"                            + " returning this view.");                }            }        }        if (holder == null) { // fallback to pool            if (DEBUG) {                Log.d(TAG, "tryGetViewHolderForPositionByDeadline("                        + position + ") fetching from shared pool");            }            // 从mRecycledViewPool中查找            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(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");            }        }    }    // This is very ugly but the only place we can grab this information    // before the View is rebound and returned to the LayoutManager for post layout ops.    // We don't need this in pre-layout since the VH is not updated by the LM.    if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {        holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);        if (mState.mRunSimpleAnimations) {            int changeFlags = ItemAnimator                    .buildAdapterChangeFlagsForAnimations(holder);            changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;            final ItemAnimator.ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,                    holder, changeFlags, holder.getUnmodifiedPayloads());            recordAnimationInfoIfBouncedHiddenView(holder, info);        }    }    boolean bound = false;    if (mState.isPreLayout() && holder.isBound()) {        // do not update unless we absolutely have to.        holder.mPreLayoutPosition = position;    } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {        if (DEBUG && holder.isRemoved()) {            throw new IllegalStateException("Removed holder should be bound and it should"                    + " come here only in pre-layout. Holder: " + holder);        }        final int offsetPosition = mAdapterHelper.findPositionOffset(position);        // 调用mAdapter.bindViewHolder绑定ViewHolder        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);    }    final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();    final LayoutParams rvLayoutParams;    if (lp == null) {        rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();        holder.itemView.setLayoutParams(rvLayoutParams);    } else if (!checkLayoutParams(lp)) {        rvLayoutParams = (LayoutParams) generateLayoutParams(lp);        holder.itemView.setLayoutParams(rvLayoutParams);    } else {        rvLayoutParams = (LayoutParams) lp;    }    rvLayoutParams.mViewHolder = holder;    rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;    return holder;}


为了方便理解代码逻辑,画了个流程图如下所示:


缓存池

现在来看看RecycledViewPool的实现。

public static class RecycledViewPool {    private static final int DEFAULT_MAX_SCRAP = 5;static class ScrapData {    ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();    int mMaxScrap = DEFAULT_MAX_SCRAP;    long mCreateRunningAverageNs = 0;    long mBindRunningAverageNs = 0;}SparseArray<ScrapData> mScrap = new SparseArray<>();    /**     * 从缓存池获取一个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;    }    /**     * viewType对应的中ScrapData添加ViewHolder     */    public void putRecycledView(ViewHolder scrap) {        final int viewType = scrap.getItemViewType();        final ArrayList 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();        scrapHeap.add(scrap);    }    /**     * 根据viewType获取ScrapData     */    private ScrapData getScrapDataForType(int viewType) {        ScrapData scrapData = mScrap.get(viewType);        if (scrapData == null) {            scrapData = new ScrapData();            mScrap.put(viewType, scrapData);        }        return scrapData;    }}

RecycledViewPool是由SparseArray实现,其中包含了多个viewType对应的ArrayList集合。获取时通过viewType得到对应的ArrayList集合,之后返回一个ViewHolder,并在集合中删除这个ViewHolder。添加操作也是类似,只不过在发现没有viewType对应的ArrayList集合时,将进行创建,并且在比较大小不超过默认大小5时,添加进入ArrayList。

总结

本文分析了RecyclerView的缓存实现,其通过多级缓存的方式实现了ViewHolder的重用,减少了ViewHolder创建,提升了效率



1 0
原创粉丝点击