RecyclerView 的缓存机制 简述
一句话概括:RecyclerView 会把滑出屏幕的 View 缓存起来,等需要显示时复用这些 View,从而提高性能。
-
为什么要缓存:
- 如果不缓存,每次滑动都会执行操作,这些操作都非常昂贵,滑动必卡
- new View
- inflate XML
- 创建 ViewHolder
- findViewById
- 优化的核心:尽量复用已有 View、尽量少走 onCreateViewHolder
- 如果不缓存,每次滑动都会执行操作,这些操作都非常昂贵,滑动必卡
-
RecyclerView 的四级缓存结构优先级从高到低:
- 一级缓存: mAttachedScrap
- 解释:几乎是“原地复用”,当前屏幕内 或 刚刚滑出屏幕,View 仍然 attach 在 RecyclerView 上,没有detach
- 特点:不需要重新绑定数据、不需要重新 layout、性能最好
- 使用场景:同一个位置的 View 重新布局、notifyItemChanged 等局部刷新
- 开发时注意什么:尽量使用 细粒度的 notify
notifyItemChanged优先走 局部刷新,不要滥用notifyDataSetChanged()
- 二级缓存: mCachedViews
- 解释:刚刚滑出屏幕、已经 detach数据仍然是有效的(valid)
- 特点:不需要重新 bind(不走
onBindViewHolder)、直接复用 ViewHolder、默认最多缓存 2 个 - 使用场景:快速上下滑动、来回滑动同一批 item
- 为什么默认两个:为了在性能和内存之间取得平衡,快速滑动的场景通常只需要少量缓存
- 开发时注意:二级缓存里的 ViewHolder 在再次使用时,可能不会重新走 onBindViewHolder,因此它会保留上一次绑定时的所有 UI 状态。
如果在第一次绑定时没有把 UI 状态设置完整(比如只处理了某些分支,没有明确设置默认状态),那么这些残留状态就会在后续复用中一直存在,导致显示错乱。
所以在 onBindViewHolder 中,必须基于数据完整、明确地设置 View 的所有可变状态,而不能依赖 View 的历史状态
- 三级缓存: ViewCacheExtension(可选)
- 解释:官方预留的扩展点、开发者可以自定义 View 缓存策略
- 使用场景:99% 项目不会用、使用不当反而引发 Bug
- 四级缓存: RecycledViewPool
- 解释:是真正的“回收池”、按 viewType 分类缓存 ViewHolder、可以被 多个 RecyclerView 共享
- 特点:会重新走
onBindViewHolder、不会重新走onCreateViewHolder、默认每种 viewType 缓存 5 个 - 开发注意什么:
- 嵌套 RecyclerView 一定要用共享 Pool,不共享的话 每个子 RecyclerView 各自 inflate、内存飙升、卡顿
- 合理调整池大小(而不是盲目增大)
pool.setMaxRecycledViews(TYPE_BANNER, 3),盲目增大View 长期存活、Bitmap / Surface 占用内存、OOM 风险;简单布局可以调大(同一类型viewType 出现多这样的),复杂布局(视频动画)不能调太大
- 前四级都没命中,才会创建新 View,ViewHolder 是性能最差的路径,因为涉及布局加载(
inflate)、构造 ViewHolder
- 一级缓存: mAttachedScrap
-
滑动view时,一个view的一生
创建 ↓ onCreateViewHolder ↓ onBindViewHolder ↓ Attached(显示中) ↓ 滑出屏幕 ↓ mCachedViews ↓ RecycledViewPool ↓ 再次复用(重新 bind)
2859

被折叠的 条评论
为什么被折叠?



