RecyclerView的缓存真是4级吗?

作者: 魏树鑫 | 来源:发表于2019-05-20 16:11 被阅读10次

    网上的大多数博客都是认为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

    相关文章

      网友评论

        本文标题:RecyclerView的缓存真是4级吗?

        本文链接:https://www.haomeiwen.com/subject/rxtkzqtx.html