分析RecyclerView
缓存机制源码的一系列文章,分析的很清楚:
RecycleView的四级缓存是由三个类共同作用完成的,Recycler
、RecycledViewPool
和ViewCacheExtension
。
用于管理已经废弃或者与RecyclerView
分离的ViewHolder
,这里面有两个重要的成员
屏幕内缓存:屏幕内缓存指在屏幕中显示的ViewHolder
,这些ViewHolder
会缓存在mAttachedScrap
、mChangedScrap
中。
scrap是用来保存被RecyclerView
移除掉但最近又马上要使用的缓存,比如说RecyclerView
中自带item的动画效果,本质上就是计算item的偏移量然后执行属性动画的过程,这中间可能就涉及到需要将动画之前的item保存下位置信息,动画后的item再保存下位置信息,然后利用这些位置数据生成相应的属性动画。如何保存这些viewholer呢,就需要使用到scrap了,因为这些viewholer数据上是没有改变的,只是位置改变而已,所以放置到scrap最为合适。稍微仔细看的话就能发现scrap缓存有两个成员mChangedScrap
和mAttachedScrap
,它们保存的对象有些不一样,一般调用adapter
的notifyItemRangeChanged
被移除的viewholder会保存到mChangedScrap
,其余的notify系列方法(不包括notifyDataSetChanged
)移除的viewholder会被保存到mAttachedScrap
中。
屏幕外缓存:当列表滑动出了屏幕时,ViewHolder
会被缓存在 mCachedViews
,其大小由mViewCacheMax
决定,默认DEFAULT_CACHE_SIZE为2,可通过Recyclerview.setItemViewCacheSize()
动态设置。
所起到的作用就是RecyclerView
滑动时刚被移出屏幕的viewholer的收容所,因为RecyclerView
会认为刚被移出屏幕的viewholder可能接下来马上就会使用到,所以不会立即设置为无效viewholer,会将它们保存到cached中,但又不能将所有移除屏幕的viewholder都视为有效viewholer,所以它的默认容量只有2个。
开发者可自定义的一层缓存,是虚拟类ViewCacheExtension
的一个实例,开发者可实现方法getViewForPositionAndType(Recycler recycler, int position, int type)
来实现自己的缓存。
RecycledViewPool
类是用来缓存ViewHolder
用,如果多个RecyclerView
之间用setRecycledViewPool(RecycledViewPool)
设置同一个RecycledViewPool
,他们就可以共享ViewHolder
。
//1.一级缓存:mAttachedScrap 一级缓存中用来存储屏幕中显示的ViewHolde
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
//2.二级缓存:mCacheViews 用来存储屏幕外的缓存
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
//3.三级缓存:mViewCacheExtension 根据coder自己定义的缓存规则
private ViewCacheExtension mViewCacheExtension;
//4.四级缓存:mRecyclerPool 当屏幕外缓存的大小大于2,便放入mRecyclerPool中缓存。
RecycledViewPool mRecyclerPool;
Recycler有4个层次用于缓存ViewHolder对象,优先级从高到底依次为ArrayList mAttachedScrap、ArrayList mCachedViews、ViewCacheExtension mViewCacheExtension、RecycledViewPool mRecyclerPool。如果四层缓存都未命中,则重新创建并绑定ViewHolder对象
缓存 | 重新创建ViewHolder |
重新绑定数据 |
---|---|---|
mAttachedScrap | false | false |
mCachedViews | false | false |
mRecyclerPool | false | true |
mAttachedScrap
:没有大小限制,但最多包含屏幕可见表项。
mCachedViews
:默认大小限制为2,放不下时,按照先进先出原则将最先进入的ViewHolder
存入回收池以腾出空间。
mRecyclerPool
:对ViewHolder
按viewType
分类存储(通过SparseArray
),同类ViewHolder
存储在默认大小为5的ArrayList
中。
mAttachedScrap
:用于布局过程中屏幕可见表项的回收和复用。
mCachedViews
:用于移出屏幕表项的回收和复用,且只能用于指定位置的表项,有点像“回收池预备队列”,即总是先回收到mCachedViews
,当它放不下的时候,按照先进先出原则将最先进入的ViewHolder
存入回收池。
mRecyclerPool
:用于移出屏幕表项的回收和复用,且只能用于指定viewType
的表项
mAttachedScrap
:ArrayList
mCachedViews
:ArrayList
mRecyclerPool
:对ViewHolder
按viewType
分类存储在SparseArray
中,同类ViewHolder
存储在ScrapData
中的ArrayList
中
第一级缓存执行流程
RecyclerView.scrollBy()–>RecyclerView.scrollByInternal()–>RecyclerView.resumeRequestLayout()–>RecyclerView.dispatchLayout()–>RecyclerView.dispatchLayoutStep1()–>LayoutManager.onLayoutChildren()–>LayoutManager.scrapOrRecycleView()–>Recycler.scrapView()
第二级缓存执行流程 执行条件 第一级缓存不要的才给第二级缓存
Recycler.recycleViewHolderInternal()
第四级缓存 第二级缓存满了 才给第四级缓存
LinearLayoutManager.onLayoutChildren()–>LinearLayoutManager.fill()–>LinearLayoutManager.layoutChunk()–>LayoutState.next()–>Recycler.getViewForPosition()–>Recycler.getViewForPosition()–>Recycler.tryGetViewHolderForPositionByDeadline()
自定义LayoutManager的流程
一些开源LayoutManager
阿里vlayout实现原理
SnapHelper这个辅助类,用于确定RecyclerView滚动停止后的目标位置、离目标View最近的Item以及目标View的position。
RecyclerView + SnapHelper实现类似ViewPager效果
关于 GridLayoutManager 的 SpanSizeLookup
阿里的三轮面试题,每次面试都问到了RecyclerView,要进阿里的看看我的总结。