android RecyclerView缓存机制

一:前言

RecyclerView是我们开发中很常用的一个控件,但是阿简没有仔细的去了解以下其实现原理,最近正好在搞一个需求,看了下源码,一起学习下~

二:关于RecyclerView

在我们使用RecyclerView的时候不免使用Adapter去处理数据,处理完了数据之后再recyclerView.setAdapter(_)去显示相关item,可以看出,UI和数据是分开的,通过对recyclerView源码的学习,recyclerView在设计的时候也遵循这个规则

三:RecyclerView.class中Recycler内部类说明

Recycler内部类:
mAttachedScrap:缓存当前屏幕可见的ViewHolder
mChangedScrap:ViewHolder更新的时候会保存在这个ArrayList里面
mCachedViews:缓存滑动时即将与RecyclerView分离的ViewHolder,按子View的position或id缓存,默认最多存放2个
mViewCacheExtension:用户自定义缓存
mRecyclerPool:缓存池(后面说明)

    public final class Recycler {
        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);

        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
        int mViewCacheMax = DEFAULT_CACHE_SIZE;

        RecycledViewPool mRecyclerPool;

        private ViewCacheExtension mViewCacheExtension;

        static final int DEFAULT_CACHE_SIZE = 2;

四:什么时候会使用到缓存机制?入口在哪?

4.1:
RecyclerView是一个自定义viewGroup,所以我们从onTouchEvent(MotionEvent event),当event等于MotionEvent.ACTION_MOVE就是滑动的时候开始分析:

            case MotionEvent.ACTION_MOVE: {
               ...省略
                    if (scrollByInternal(
                            canScrollHorizontally ? dx : 0,
                            canScrollVertically ? dy : 0,
                            vtev)) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
                        mGapWorker.postFromTraversal(this, dx, dy);
                    }
                }
            } break;

我们来看一下从scrollByInternal()一直点进去之后的调用链
OnTouch --> scrollByInternal --> scrollStep --> mLayout.scrollVerticallyBy --> scrollBy --> fill --> layoutChunk --> layoutState.next --> getViewForPosition --> tryGetViewHolderForPositionByDeadline

4.2:
当RecyclerView中item发生变化的时候,就是Adapter调用了notifydatasetchanged这些一系列的方法时候,会触发以下调用链:
notifyDataSetChanged-->mObservable.notifyChanged --> (RecyclerViewDataObserver)mObservers.get(i).onChanged --> requestLayout
可以看到最后调用到了requestLayout,所以会重新执行RecyclerView的onMeasure-onLayout-onDraw方法,然后就会开始调用缓存操作:
onMeasure()/onLayout() -->dispatchLayoutStep1()/dispatchLayoutStep2()--> mLayout.onLayoutChildren -->onLayoutChildren() -->fill()->layoutChunk() -->next() -->getViewForPosition()-->tryGetViewHolderForPositionByDeadline

五:缓存机制分析

由第四步可知最后都是调用到了fill()->layoutChunk() -->next() -->getViewForPosition()-->tryGetViewHolderForPositionByDeadline
那我们来看tryGetViewHolderForPositionByDeadline做了什么(这个方法也是我们常说的四级缓存的实现)

//根据传入的position获取ViewHolder 
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
        boolean dryRun, long deadlineNs) {
    ...省略    
    boolean fromScrapOrHiddenOrCache = false;
    ViewHolder holder = null;
    //预布局 属于特殊情况 从mChangedScrap中获取ViewHolder
    if (mState.isPreLayout()) {
        holder = getChangedScrapViewForPosition(position);
        fromScrapOrHiddenOrCache = holder != null;
    }
    if (holder == null) {
        //1、尝试从mAttachedScrap中获取ViewHolder,此时获取的是屏幕中可见范围中的ViewHolder
        //2、mAttachedScrap缓存中没有的话,继续从mCachedViews尝试获取ViewHolder
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
     ...省略
    }
    if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        ...省略
        final int type = mAdapter.getItemViewType(offsetPosition);
        //如果Adapter中声明了Id,尝试从id中获取,这里不属于缓存
        if (mAdapter.hasStableIds()) {
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                    type, dryRun);
        }
        if (holder == null && mViewCacheExtension != null) {
            3、从自定义缓存mViewCacheExtension中尝试获取ViewHolder,该缓存需要开发者实现
            final View view = mViewCacheExtension
                    .getViewForPositionAndType(this, position, type);
            if (view != null) {
                holder = getChildViewHolder(view);
            }
        }
        if (holder == null) { // fallback to pool
            //4、从缓存池mRecyclerPool中尝试获取ViewHolder
            holder = getRecycledViewPool().getRecycledView(type);
            if (holder != null) {
                //如果获取成功,会重置ViewHolder状态,所以需要重新执行Adapter#onBindViewHolder绑定数据
                holder.resetInternal();
                if (FORCE_INVALIDATE_DISPLAY_LIST) {
                    invalidateDisplayListInt(holder);
                }
            }
        }
        if (holder == null) {
            ...省略
          //5、若以上缓存中都没有找到对应的ViewHolder,最终会调用Adapter中的onCreateViewHolder创建一个
            holder = mAdapter.createViewHolder(RecyclerView.this, type);
        }
    }
 
    boolean bound = false;
    if (mState.isPreLayout() && holder.isBound()) {
        holder.mPreLayoutPosition = position;
    } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        //6、如果需要绑定数据,会调用Adapter#onBindViewHolder来绑定数据
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
    }
    ...省略
    return holder;
}

可见以下流程图:


六:RecyclerView.class中RecycledViewPool内部类说明

通过上一步的分析可知,前面三级缓存是不用重新绑定viewHolder的,只有从RecycledViewPool中获取到的RecycledViewPool需要重新绑定数。
通过下面一种场景可以帮助理解:如一个APP的主界面ViewPager有多个fragment,其中某两个fragment都会用到RecyclerView,而且其中的一些item还是相同的,那么此时我们为了节约内存,就可以使用
RecycledViewPool让这两个RecyclerView共享同一个四级缓存pool

RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
MyAdapter myAdapter = new MyAdapter(datas);
RecyclerView.ViewHolder viewHolder = myAdapter.createViewHolder(recyclerView, 0);
pool.putRecycledView(viewHolder);
recyclerView.setRecycledViewPool(pool);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(myAdapter);

END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容