【腾讯Bugly干货分享】Android ListView 与 RecyclerView 对比浅析—缓存机制
自定义控件三部曲视图篇(七)——RecyclerView系列之四实现回收复用
RecyclerView的回收复用由模块Recycler来负责,其设计了4层缓存,
按照使用的优先级顺序依次是
- mAttachedScrap ( onLayoutChildren方法中调用detachAndScrapAttachedViews(recycler) )
- mCachedViews (scrollVerticallyBy/scrollHorizontallyBy 方法中调用removeAndRecycleView(child, recycler),默认2个 )
- mViewCacheExtension ( ViewCacheExtension默认是没有实现的,预留给开发者针对自己的项目实际使用。)
- mRecyclerPool (按照viewtype将以上尚未存储的进行存储)
Recycler:
public final class Recycler {
//1
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
//2
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;
//3
RecycledViewPool mRecyclerPool;
//4
private ViewCacheExtension mViewCacheExtension;
static final int DEFAULT_CACHE_SIZE = 2;
}
Scrap
scrap 英 [skræp] 美 [skræp]
n. 碎片,小块(纸、织物等); 丝毫; 一丁点; 残羹剩饭
Scrap是RecyclerView中最轻量的缓存,它不参与滑动时的回收复用,只是作为重新布局时的一种临时缓存。
它的目的是,缓存当界面重新布局的前后都出现在屏幕上的ViewHolder,以此省去不必要的重新加载与绑定工作。
Scrap实际上包括了两个ViewHolder类型的ArrayList。
重新布局临时缓存, 【显示在屏幕中】,【不会createViewHolder和bindViewHolder】
- mAttachedScrap : 负责保存将会【原封不动】的ViewHolder,如:初始化时,或者移除其他item自己没动
- mChangedScrap : 负责保存【位置会发生移动】的ViewHolder,注意只是位置发生移动,内容仍旧是原封不动的。
在layoutmanager的onLayoutChildren初始布局时:使用 detachAndScrapAttachedViews(recycler)将所有的可见HolderView存储到mAttachedScrap
mCachedViews
mCachedViews: 【位置移出屏幕进行的缓存】,【默认缓存2个】,【不会createViewHolder和bindViewHolder】
CacheView是一个以ViewHolder为单位,负责在RecyclerView列表位置产生变动的时候,对刚刚移出屏幕的View进行回收复用的缓存列表。
removeAndRecycleView(child, recycler)
这个函数仅用于滚动的时候,在滚动时,我们需要把滚出屏幕的HolderView标记为Removed,
这个函数的作用就是把已经不需要的HolderView标记为Removed。
想必大家在理解了上面的回收复用原理以后,也知道在我们把它标记为Removed以后,系统做了什么事了。
在我们标记为Removed以为,会把这个HolderView移到mCachedViews中,如果mCachedViews已满,就利用先进先出原则,
将mCachedViews中老的holderView移到mRecyclerPool中,然后再把新的HolderView加入到mCachedViews中。
在滚动时,所有移除的View都是使用removeAndRecycleView(child, recycler),
千万不要将它与detachAndScrapAttachedViews(recycler)搞混了。
在滚动时,已经超出边界的HolderView是需要被回收的,而不是被detach。
detach的意思是暂时存放,立马使用。
很显然,我们这里在越界之后,立马使用的可能性不大,所以必须回收。
如果立马使用,它会从mCachedViews中去取。
大家也可以简单的记忆,在onLayoutChildren函数中(布局时),就使用detachAndScrapAttachedViews(recycler),
在scrollVerticallyBy函数中(滚动时),就使用removeAndRecycleView(child, recycler),当然能理解就更好啦。
mRecyclerPool
mRecyclerPool : 以ViewHolder的viewType来缓存,【默认缓存5个】,【会bindViewHolder】,
mCachedViews超出上限后执行mRecyclerPool存储,即Scrap和cache不存储之后执行pool存储。
RecycledViewPool:
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
private int mAttachCount = 0;
}