美文网首页
RecycleView四级缓存

RecycleView四级缓存

作者: 资本家大恶人 | 来源:发表于2020-09-13 18:24 被阅读0次

    [图片上传失败...(image-3b4bad-1599992637692)]

    |层级 |缓存变量 |缓存名| 容量 |数据结构 |缓存用途|

    • Scrap

    对应ListView 的Active View,就是屏幕内的缓存数据,就是相当于换了个名字,可以直接拿来复用。

    • Cache

    刚刚移出屏幕的缓存数据,默认大小是2个,当其容量被充满同时又有新的数据添加的时候,会根据FIFO原则,把优先进入的缓存数据移出并放到下一级缓存中,然后再把新的数据添加进来。Cache里面的数据是干净的,也就是携带了原来的ViewHolder的所有数据信息,数据可以直接来拿来复用。需要注意的是,cache是根据position来寻找数据的,这个postion是根据第一个或者最后一个可见的item的position以及用户操作行为(上拉还是下拉)。
    举个栗子:当前屏幕内第一个可见的item的position是1,用户进行了一个下拉操作,那么当前预测的position就相当于(1-1=0),也就是position=0的那个item要被拉回到屏幕,此时RecyclerView就从Cache里面找position=0的数据,如果找到了就直接拿来复用。

    • ViewCacheExtension

    是google留给开发者自己来自定义缓存的,这个ViewCacheExtension我个人建议还是要慎用,因为我扒拉扒拉网上其他的博客,没有找到对应的使用场景,而且这个类的api设计的也有些奇怪,只有一个public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position, int type);让开发者重写通过position和type拿到ViewHolder的方法,却没有提供如何产生ViewHolder或者管理ViewHolder的方法,给人一种只出不进的赶脚,还是那句话慎用。

    • RecycledViewPool

    刚才说了Cache默认的缓存数量是2个,当Cache缓存满了以后会根据FIFO(先进先出)的规则把Cache先缓存进去的ViewHolder移出并缓存到RecycledViewPool中,RecycledViewPool默认的缓存数量是5个。RecycledViewPool与Cache相比不同的是,从Cache里面移出的ViewHolder再存入RecycledViewPool之前ViewHolder的数据会被全部重置,相当于一个新的ViewHolder,而且Cache是根据position来获取ViewHolder,而RecycledViewPool是根据itemType获取的,如果没有重写getItemType()方法,itemType就是默认的。因为RecycledViewPool缓存的ViewHolder是全新的,所以取出来的时候需要走onBindViewHolder()方法。
    再来张图看看整体流程

    [图片上传失败...(image-ceed30-1599992637693)]

    回收的关键方法分析

    ## 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

    从一级缓存 mChangeScrap 中取

    从二级缓存 mCachedViews中取

    从三级缓存 mViewCacheExtension 中取

    从四级缓存 缓存池中取

    缓存中都没有拿到值,就直接创建

    未绑定过时,进行绑定

    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 中取 
    // 这里开始拿第三级缓存了,这里我们不自定义ViewCacheExtension就不会进入判断条件,还是那句话慎用。
                    if (holder == null && mViewCacheExtension != null) {
                        final View view = mViewCacheExtension
                                .getViewForPositionAndType(this, position, type);
                        if (view != null) {
                            holder = getChildViewHolder(view);
                        }
                    }
                    // 4) 从四级缓存 缓存池中取
    //这里到了第四级缓存RecycledViewPool,
    //getRecycledViewPool().getRecycledView(type);通过type拿到
    //ViewHolder,接着holder.resetInternal();重置ViewHolder,让其
    //变成一个全新的ViewHolder
                    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;
    

    RecyclerView有四级缓存,分别是Scrap、Cache、ViewCacheExtension和RecycledViewPool,缓存的对象是ViewHolder。Scrap和Cache分别是通过position去找ViewHolder可以直接复用;ViewCacheExtension自定义缓存,目前来说应用场景比较少却需慎用;RecycledViewPool通过type来获取ViewHolder,获取的ViewHolder是个全新,需要重新绑定数据。当你看到这里的时候。

    相关文章

      网友评论

          本文标题:RecycleView四级缓存

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