RecyclerView缓存机制
RecyclerView缓存机制
对于RecyclerView的缓存机制,首先要考虑一下几个问题:
- 回收什么?复用什么?
- 回收到哪里去?从哪里获得复用?
- 什么时候回收?什么时候复用?
带着这几个问题,去看源码,相信如果能准确的回答这几个问题,那么对RecyclerView的回收机制,基本就了解清楚了。
回收什么?复用什么?
先回答这个问题,回收的是ViewHolder,复用的也是ViewHolder
回收到哪里去?从哪里获得复用?
这个分为两部分:
1,回收到哪里去?
我个人习惯定义为四级缓存:
- mChangeScrap与 mAttachedScrap,用来缓存还在屏幕内的 ViewHolder
- mCachedViews,用来缓存移除屏幕之外的 ViewHolder
- mViewCacheExtension,开发给用户的自定义扩展缓存,需要用户自己管理 View 的创建和缓存
- RecycledViewPool,ViewHolder 缓存池,它的数据都是从mCachedViews中转移过来的
回收,就是把ViewHolder回收到以上四级缓存中去,这四级缓存都是用来存放ViewHolder的集合。
2,从哪里获得复用?
获得复用,也就是从上面四级缓存中,获得保存的ViewHolder,展示在RecyclerView列表中。
3,什么时候回收?什么时候复用?
这个点是最关键的,要了解触发缓存回收的动作是什么,什么时候需要RecyclerView去从缓存中获取这些ViewHolder进行复用。
关于这个问题,从以下几点着手分析:
- 系统绘制RecyclerView时
- 滑动RecyclerView时
- 当RecyclerView列表中,有某些或者某个单独的Item发生变化,需要通过Adapter的notifyxxx方法更新界面的时候
以上这三点都会触发回收,其中3在触发回收的时候,同时也是从缓存中获取ViewHolder进行复用的时候。
3.1系统绘制RecyclerView时回收
系统绘制RecyclerView时,onMeasure-->onLayout-->onDraw,无非就是这几个方法,看源码流程,大概如下:
onLayout-->dispatchLayout-->dispatchLayoutStep2-->LinearLayoutManager.onLayoutChildren --> detachAndScrapAttachedViews --> scrapOrRecycleView
//从这里开始,主要分为两个分支:
--> 1.recycler.recycleViewHolderInternal(viewHolder); -- 处理 CacheView 、RecyclerViewPool 的缓存
--> 1.1 ViewHodler改变 不会进来 -- 先判断mCachedViews的大小
--> mCachedViews.size 大于默认大小 --- recycleCachedViewAt --- >addViewHolderToRecycledViewPool --- 缓存池里面的数据都是从mCachedViews里面出来的
--> 1.2 addViewHolderToRecycledViewPool --> getRecycledViewPool().putRecycledView(holder);
--> scrap.resetInternal(); ViewHolder 清空
--> 2.recycler.scrapView(view);
下面是这整个过程的时序图:
3.2滑动RecyclerView时,会有复用,复用的时候也就是从四级缓存的几个集合中去获取ViewHolder进行复用
滑动 Move 事件,OnTouch --> scrollByInternal --> scrollStep --> mLayout.scrollVerticallyBy --> scrollBy --> fill --> layoutChunk --> layoutState.next --> addView(view);
layoutState.next --> getViewForPosition --> tryGetViewHolderForPositionByDeadline -->
怎么从集合中去获取:tryGetViewHolderForPositionByDeadline,分几种情况去获取ViewHolder
-
getChangedScrapViewForPosition -- mChangeScrap 与动画相关
-
getScrapOrHiddenOrCachedHolderForPosition -- mAttachedScrap 、mCachedViews
-
getScrapOrCachedViewForId -- mAttachedScrap 、mCachedViews (ViewType,itemid)
-
mViewCacheExtension.getViewForPositionAndType -- 自定义缓存 -- (使用情况:局部刷新??)
-
getRecycledViewPool().getRecycledView -- 从缓冲池里面获取, RecycledViewPool -- 缓存池
6.当没有缓存的时候---> mAdapter.createViewHolder --> onCreateViewHolder
多级缓存的目的 -- 为了性能
创建ViewHolder 后 绑定: tryBindViewHolderByDeadline--> mAdapter.bindViewHolder-->onBindViewHolder
整个流程调用时序图如下:
3.3 当RecyclerView列表中,有某些或者某个单独的Item发生变化,需要通过Adapter的notifyxxx方法更新界面的时候
主要流程如下:notifyDataSetChanged-->mObservable.notifyChanged --> (RecyclerViewDataObserver)mObservers.get(i).onChanged --> requestLayout
流程调用时序图: