带着问题学习
RecyclerView有几级缓存?分别是什么?
RecyclerView各级缓存的作用?
RecyclerView缓存间的关系?
- 为什么有的缓存取出来不用重新执行
onBindViewHolder(),而有的需要?
RecyclerView 的四级缓存
Google 官方不把 Scrap 算入缓存,但是,因为在加载 ViewHolder 时,有从 Scrap 中获取的步骤,所以,个人认为将其当成第一级缓存是比较合适的
Scrap:mAttachedScrap、mChangedScrap
CachedView
ViewCacheExtension
RecycledViewPool
Scrap
scrap 即作废、废弃的意思 用于缓存在屏幕上显示的 ViewHolder 无默认大小,屏幕上有多少个 ViewHolder,就有多大
什么时候会触发该层缓存机制?
- 该层缓存机制应该是一直存在的,也就是,不管
scarp中是否有缓存,每次调用next()方法时,都会优先从scarp层中尝试获取缓存进行复用。
- 而在调用
LayoutManager.onLayoutChildren()或notifyItemXXX()方法时,会把ViewHolder往mAttachedScrap或mChangedScrap中存放。
为什么在屏幕上显示的 ViewHolder,也要被缓存,而且还是 被废弃 后缓存起来的呢?
- 因为在
RecyclerView显示的过程中,每次更新,都会把所有的ViewHolder都从屏幕上分离开,也就是,每次更新的时候,都会把正在显示的View都标记为 Scrap/废弃 状态
- 既然每次都把所有的
ViewHolder从屏幕上分离开,自然就要将其 缓存 起来,方便刷新后复用,因为两次相邻的刷新之间,ViewHolder的相似度还是很高的,如果每次都重新走一遍onCreateViewHolder()和onBindViewHolder()的话,那就太浪费资源了。
那为什么 Scrap 要有 attachedScrap() 和 changedScarp() 两个不同的缓存列表呢?
- 因为对于在
RecyclerView列表中的每个ViewHolder来说,在刷新时,无非就是 能否直接复用 两种状态
- 能直接复用的:放入
mAttachedScrap中,后面直接复用
- 不能直接复用的:放入
mChangedScrap中,复用时需要调用onBindViewHolder()重新绑定数据
决定放入 mAttachedScrap 或 mChangedScrap 的策略是什么?
mAttachedScrap:调用notifyItemRemoved、notifyItemMoved、notifyItemInserted之类的方法时,会把ViewHolder放入mAttachedScrap中
mChangedScrap:调用notifyItemChanged()、notifyItemRangeChanged()之类的方法时,会把ViewHolder放入mChangedScrap中
- 直接看源码:
什么时候从 mAttachedScrap 中获取?
- 当缓存起来的
ViewHodler的position与RecyclerView中要加载的position对应上的时候,就从mAttachedScrap中获取缓存并返回
- 看源码:
什么时候从 mChangedScrap 中获取?
- 如果
mAttachedScrap没有命中缓存,且当缓存起来的ViewHodler的position或id与RecyclerView中要加载的position或id对应上的时候,就从mChangedScrap中获取缓存并返回
- 看源码
CachedView
用来缓存已经滑出或即将滑进屏幕的 ViewHolder 有默认大小,为 2 在这里的缓存,不会擦除数据,所以复用时候,不需要重新onBindViewHolder()
CachedView 是什么结构,其最大容量为多少?
- 一个
ArrayList,具体看源码定义
什么时候会加入 mCachedView?mCachedView 满了之后,应该怎么办?
- 当
ViewHolder滑出屏幕时,会把滑出的ViewHolder加入到mCachedView中
- 如果
mCachedView满了,就需要将下标为 0 的ViewHolder加入mRecyclerPool中,并在mCachedView中将其移除(之所以不是移除最先加入mCachedView的元素,是因为,先加入的ViewHolder有可能是pre-fetch出来的ViewHolder,而在插入ViewHolder时,是会跳过改元素,并插入到其前面的)
- 需要注意的是,如果是对
ViewHolder的状态是REMOVE、INVALID、UPDATE、ADAPTER_POSITION_UNKNOWN时,是不会将其加入mCachedView中的
根据什么判断 CachedView 中的 ViewHodler 可以被复用?
- 根据
ItemId及ItemViewType是否与要加载的ViewHolder相同,相同就复用,不相同就不复用
- 看源码
什么时候从 mCachedView 中获取缓存?
- 当从
Scrap中获取不到时,则尝试从mCachedView中获取
ViewCacheExtension
Google 官方预留出来的给开发者用于自定义缓存策略的扩展接口
RecycledViewPool
第四级缓存,根据viewType进行缓存,每个viewType都可以指定最大缓存个数 这一级的缓存里面,会擦除掉 ViewHolder 中的数据,也就是复用时候需要执行onBindViewHolder()重新绑定数据
如果要在多个 RecyclerView 中共享同一个 RecycledViewPool,应该怎么做?
- 在使用
RecyclerView时,指定相应的RecycledViewPool
- 看源码
RecycledViewPool 的数据结构是怎么样的?
- 具体是一个以
viewType为key的SparseArray(一个更加高效的Map),value是一个ArrayList<ViewHolder>
- 看源码
如果对于一个 ViewType 的缓存个数,超过了 DEFAULT_MAX_SCRAP,会怎么样?
- 会立即返回不进行缓存,而不是把旧的移除,因为是同一个
ViewType的ViewHolder,没有必要进行这样的操作
- 看源码
各级缓存之间的关系
- 简单来说,就是在需要
ViewHolder时,依次从第一级到第四级缓存中找,如果发现匹配的,就直接返回,否则就新建一个
- 对于在屏幕中可见的
ViewHolder,缓存在Scrap缓存中
- 对于因为滑动而消失在屏幕中的
ViewHolder,就缓存在CachedView中
- 当
CachedView中数量达到上限后,就把CachedView中下标为 0 的ViewHolder根据不同的ViewType放入RecycledViewPool中
总结
RecyclerView总共分为四级缓存,如果是在mAttachedScrap、mCachedView之中的缓存,就可以直接复用,而不需要重新走onBindViewHolder(),如果是在mChangedScrap、ViewCacheExtension、RecycledViewPool中的缓存,就需要重新走onBindViewHolder()将视图与新的数据进行绑定
参考/推荐
- Author:CoderWdd
- URL:https://www.wuinsights.top//article/f1fab704ae5f
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts


