Recyclerview的缓存机制
recycler [ri:'saikl?] 反复循环器,[化工] 再循环器
Android 新增的Recyclerview 主要用于代替ListView 。Recyclerview 可扩展性强。
- 可以通过
LayoutManager 形成线性(横向与竖向)、网格、瀑布流布局。 - 通过
OnItemTouchListener 监听Item 的事件,虽然比ListView.OnItemClickListener 麻烦了点,但是可以实现更复杂的功能,比如item 滑动。 - 提供了
notifyItemInserted 、notifyItemRemoved 、notifyItemChanged 、notifyItemMoved 来提高局部刷新的效率。 - 没有
ListView 的HeaderView 和FooterView , 但可以通过来getItemViewType 来生成不同的视图。 RecyclerView 还定义了ViewHolder ,配合RecyclerView.Adapter ,封装重用ItemView 的逻辑,还有四级缓存,效率大大增加。
1 Recyclerview 的缓存类
RecyclerView 缓存基本上是通过三个内部类管理的,Recycler 、RecycledViewPool 和ViewCacheExtension 。
extension [?k?sten?n] 延伸,扩展
1.1 Recycler
用于管理已经废弃或者与RecyclerView 分离的ViewHolder ,以下是内部类的成员变量和它们的含义:
Scrap 缓存用在RecyclerView 布局时,布局完成之后就会清空
添加到Cache 缓存和RecyclerViewPool 缓存的item ,它们的View 必须已经从RecyclerView 中detached 或removed
scrap [skr?p] 碎片,小块; attach [??t?t?] 系上,贴上 detached [d??t?t?t] 单独的,分离的
public class RecyclerView extends ViewGroup implements ScrollingView,
NestedScrollingChild2, NestedScrollingChild3 {
public final class Recycler {
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private ViewCacheExtension mViewCacheExtension;
RecycledViewPool mRecyclerPool;
int mViewCacheMax = DEFAULT_CACHE_SIZE;
static final int DEFAULT_CACHE_SIZE = 2;
}
}
1.2 RecycledViewPool
RecycledViewPool 类是用来缓存Item 用,是一个ViewHolder 的缓存池,如果多个RecyclerView 之间用setRecycledViewPool(RecycledViewPool) 设置同一个RecycledViewPool ,它们就可以共享Item 。其实RecycledViewPool 的内部维护了一个Map ,里面以不同的viewType 为Key 存储了各自对应的ViewHolder 集合。 可以通过提供的方法来修改内部缓存的Viewholder 。
public class RecyclerView extends ViewGroup implements ScrollingView,
NestedScrollingChild2, NestedScrollingChild3 {
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
}
}
1.3 ViewCacheExtension
开发者可自定义的一层缓存,是虚拟类ViewCacheExtension 的一个实例,开发者可实现方法getViewForPositionAndType(Recycler recycler, int position, int type) 来实现自己的缓存。
2 Recyclerview 的四级缓存
2.1 屏幕内缓存/一级缓存(mAttachedScrap 和mChangedScrap )
屏幕内缓存指在屏幕中显示的ViewHolder ,这些ViewHolder 会缓存在mAttachedScrap 、mChangedScrap 中:
mChangedScrap 表示数据已经改变的viewHolder 列表mAttachedScrap 未与RecyclerView 分离的ViewHolder 列表
ViewHolder 只有在满足下面情况才会被添加到mChangedScrap :当它关联的item 发生了变化(notifyItemChanged 或者 notifyItemRangeChanged 被调用),并且ItemAnimator 调用ViewHolder#canReuseUpdatedViewHolder 方法时,返回了false (返回false 表示我们要执行用一个view 替换另一个 view 的动画,例如淡入淡出动画。true 表示动画在view 内部发生)。否则,ViewHolder 会被添加到mAttachedScrap 中。
mChangedScrap 和mAttachedScrap 可以看做是一个层级,都是屏幕上可见itemView ,只不过区分了状态(改变和未改变)。
RecyclerView 的滑动场景来说,新卡位的复用以及旧卡位的回收机制, 不会涉及到mChangedScrap 和mAttachedScrap
可以在3 种情况下重用更新的ViewHolder :
setSupportsChangeAnimations(false) notifyDataSetChanged 而不是notifyItemChanged 或notifyItemRangeChanged notifyItemChanged(index, anyObject)
最后一种情况显示了一种很好的方法,当只想更改一些内部元素时,可以避免创建/绑定新的ViewHolder 。
2.2 屏幕外缓存/二级缓存(mCachedViews )
当列表滑动出了屏幕时,ViewHolder 会被缓存在mCachedViews ,其大小由mViewCacheMax 决定,默认DEFAULT_CACHE_SIZE 为2 ,可通过Recyclerview.setItemViewCacheSize() 动态设置。
2.3 自定义缓存/三级缓存(ViewCacheExtension )
可以自己实现ViewCacheExtension 类实现自定义缓存,可通过Recyclerview.setViewCacheExtension() 设置。
2.4 缓存池/四级缓存(RecycledViewPool )
ViewHolder 在首先会缓存在mCachedViews 中,当超过了个数(比如默认为2 ),就会添加到RecycledViewPool 中。RecycledViewPool 会根据每个ViewType 把ViewHolder 分别存储在不同的列表中,每个ViewType 最多缓存DEFAULT_MAX_SCRAP = 5 x ViewHolder ,如果RecycledViewPool 没有被多个RecycledView 共享,对于线性布局,每个ViewType 最多只有一个缓存,如果是网格有多少行就缓存多少个。它们之间的关系如下 :
2.5 缓存策略
Recyclerview 在获取ViewHolder 时按四级缓存的顺序查找,如果没找到就创建(createViewHolder )。其中只有RecycledViewPool 找到时才会调用 bindViewHolder ,其它缓存不会重新bindViewHolder 。 流程如下 :
3 总结
通过了解RecyclerView 的四级缓存,我们可以知道,RecyclerView 最多可以缓存N(屏幕最多可显示的item数) + 2(屏幕外的缓存) + 5 x M (M代表M个ViewType,缓存池的缓存) ,只有RecycledViewPool 找到时才会重新调用bindViewHolder 。 还需要注意的是,RecycledViewPool 可以被多个RecyclerView 共享,其缓存个数与ViewType 个数、布局相关,如果RecycledViewPool 没有被多个RecycledView 共享,对于线性布局,每个ViewType 最多只有一个缓存,如果是网格布局有多少行就缓存多少个。
参考
https://zhooker.github.io/2017/08/14/关于Recyclerview的缓存机制的理解/
|