RecyclerView的缓存真是4级吗?

网上的大多数博客都是认为RecyclerView是4级缓存,但真的是4级缓存吗?知道我看了源码,才发现,RecyclerView做的远远不止4级缓存,当然是根据LayoutManager不同,缓存实现也不同,本文主要分析最最常用的LinearLayoutManager。

写的有点乱,以后整理吧

RecyclerView复用过程

RecyclerView的中的缓存复用是由内部类Recycler来维护的,在RecyclerView.Adapter调用onCreateViewHolder来创建ViewHolder,这时就开始了利用缓存机制;

这里是以LinearLayoutManager为例,其他也差不多,只是一些细节不一样;
先看下调用流程吧,否则直接看缓存,也不知道什么时候调用的:
RecyclerView.png

可以看到,RecyclerView的在onMeasure时,就将item所有的操作都交给了LayoutManager,并在创建ViewHolder时,将缓存复用机制交给了RecyclerView内部类Recycler,Recycler的tryGetViewHolderForPositionByDeadline方法内,实现了所有的复用机制;

缓存实现

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级缓存;

缓存的实现

接下来我们详细看下复用机制的实现:
其实在LayoutState的next方法中就有一层复用,mScrapList实际就是mUnmodifiableAttachedScrap,这个调用是在onLayoutChildren最后的layoutForPredictiveAnimations的方法中,做动画时,直接去拿mUnmodifiableAttachedScrap中的itemView;
否则才是真正的去创建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取数据;
            if (mState.isPreLayout()) {
                //里面主要是从mChangedScrap中找
                holder = getChangedScrapViewForPosition(position);
            }
            // 1) Find by position from scrap/hidden list/cache
            if (holder == null) {
                // 里面主要从mAttachedScrap和ChildHelper的mHiddenViews还有mCachedViews中查找
                //这里面是确切的匹配,里面各种状态必须完全一致,拿出来的,不需要经过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);
                }
                //  创建新的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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值