RecyclerView 的 setItemViewCacheSize

75 阅读2分钟

它具体做了什么

  • 当某个 item 滑出屏幕时,Recycler 会先尝试把对应的 ViewHolder 放进 mCachedViews(容量由你设置决定)。

  • 以后如果又需要同一个 position(或启用 stableIds 后,同一个 id) ,会优先从这里取用

    • 命中时通常无需重新绑定(不触发 onBindViewHolder) ,只需位置复位 → 更平滑、少一次绑定开销。
  • 如果本地缓存已满,被挤出的 ViewHolder 会被放入 RecycledViewPool(按 viewType 的“冷缓存”,再次用时要 rebind)。

和其他缓存的区别(速记)

  • Scrap(本帧临时) :同一轮布局中的“原地复用”,成本最低。

  • Cache(mCachedViews,你设置的这个) :本地热缓存,按 position/id 直接复用,常可免 rebind。

  • Pool(RecycledViewPool) :跨 RV 共享、按 viewType 分桶的冷缓存,再用时需 rebind

作用与收益

  • 减少白板感、减少 onBindViewHolder 调用,提升短距离来回滚动的流畅度。

  • 绑定成本较高(复杂布局/数据组装)的条目收益明显。

代价与误区

  • 占用更多内存:缓存的每个 item 及其子 View 都会常驻(包括图片引用等)。

  • 不是预加载:它不会提前创建新的 View,只是“多留几个刚离屏的”。

  • 数据频繁变更时,这些缓存会被标记为需要重绑,不能保证永远免 onBind

什么时候调、调多少

  • 列表复杂但内存充裕:可设 4–8(甚至更高,视页面而定)。

  • 图片密集/内存紧张页面(瀑布流/大图):保持 2–4 较稳妥。

  • 结合:

    • setHasStableIds(true) 提升“同 id 免重绑”的命中率;

    • RecycledViewPool.setMaxRecycledViews(viewType, max) 调冷缓存;

    • LinearLayoutManager/嵌套 RV 配合 prefetch 做错峰绑定。

一条实践范式

recyclerView.setItemViewCacheSize(6)              // 提升本地热缓存
recyclerView.recycledViewPool.apply {
  setMaxRecycledViews(VT_BIG, 10)                 // 重型卡片冷缓存也拉高
  setMaxRecycledViews(VT_SMALL, 15)
}
adapter.setHasStableIds(true)                     // 用稳定ID提高命中

一句话:setItemViewCacheSize 决定“离屏还留几个已绑定好的 ViewHolder 在手边”。调大能换取更顺滑的回滚体验,但要用内存做代价;配合稳定 ID、DiffUtil 与 RecycledViewPool,一起调,效果最佳。