RecyclerView缓存机制
在介绍所有的缓存之前需要隆重介绍recyclerview复用机制的核心tryGetViewHolderForPositionByDeadline
ViewHolder tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) {
// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
}
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
}
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
}
}
通过上面代码片段可以看到recyclerview为了复用一共准备了四处缓存
mAttachedScrap mCachedViews mViewCacheExtension mRecyclerPool
mAttachedScrap & mChangedScrap
AttachedScrap 查询网络上已有的资料,大多数博文只会说这个缓存是用来存储界面当前正在展示的viewholder。从attachedScrap中获取到的viewholder在使用的时候不需要重新创建view也不需要重新绑定数据。
但是仔细一想,为什么处于界面之中的viewholder却要有一个缓存池来进行储存了,更何况大部分时间里该scrap一直都是空的,可当前界面明明就有viewholder展示。
为了搞懂这个问题,重新查看了源码发现,recyclerview实际上只会在view被detach的时候才会存入scrapView,比如调用了notifyXXX方法。
调用notifyItemChanged/Removed或者notifyItemRangeChanged后,被标记为删除的viewholder将会被丢入changeScrap。在此scarp中的viewholder将在下一次layout中被拿出来执行动画。当然不是所有的情况都会丢入changedScrap,当以下情况中不需要动画就不会被放入changedScrap
setSupportsChangeAnimations(false)。
notifyDataSetChanged 而不是 notifyItemChanged 或 notifyItemRangeChanged
notifyItemChanged(index,anyObject)。
mCachedViews
相比较上面的scrap,cachedViews则更加接近缓存一点。cachedView默认大小为2,当页面划动时,逐步从页面内划走的则会viewholder则会依次进入cache。处在cachedview中的viewholder不会再需要绑定或者创建。
但是,这些viewholder依旧会执行onViewAttachedToWindow和onDetachFromWindow。
此外,cachedview还会存储pre-layout出来的viewholder。且当有pre-layout完成时,recyclerview会修改cachedviewSize,所以pre-layout出来的holder不会占用设置给cachedView的默认大小。
当cacheview存满时,viewholder则会按照先进先出的顺序,一次进入下一级缓存 recyclerPool。
ViewCacheExtension
viewcacheExtension是完全给开发者开放出来的,recyclerview没有给任何的默认实现,只负责取出展示。所以具体的缓存规则包括存入的时机都需要开发者自己完成。
特殊的点是,viewCacheExtension返回的是view,然后从view中的layoutParams中取出viewholder。
然后viewholder本身会存储一些flag,这些flag被recycerview管理。如果没有管理很好会造成crash。
至于具体实现在网上很少有优秀的实践例子,不到万不得已都不推荐使用该拓展。
mRecyclerPool
recyclerPool是实际过程里这几种缓存中使用频率最高的缓存。cacheview中存不下的viewholder就会被存在这里。recyclerPool默认每一种type类型的viewhodler最多存储5个,对于存不下的viewholder则会进行丢弃。存在pool中的holder拿出来后不需要进行再次createView,但是任然需要进行bindholder来进行数据绑定。但其实已经会节省大部分时间提高系统的流畅度了。
总结
recyclerview的各种缓存需要结合具体的实际情况,进行合适的调用与调整,在内存和效率间取到一个平衡。如果贸然的增大缓存可能会得不偿失,但不好好利用缓存机制,应用又会非常卡顿,本文只是简单介绍了缓存,recyclerview还有pre-load机制、viewcacheExtension细节等。也依旧值得去继续学习。
网友评论