RecyclerView

RecyclerView 缓存机制 | 如何复用表项? - 掘金

RecycledViewPool中的ViewHolder存储在SparseArray中,并且按viewType分类存储(即是Adapter.getItemViewType()的返回值),同一类型的ViewHolder存放在ArrayList 中,且默认最多存储5个。

 在mRecyclerPool成功获得ViewHolder对象后,立即调用holder.resetInternal();对其重置(将flag置0)。这样就满足了绑定数据的判断条件(因为0和非0位与之后必然为0)。 所以,mRecyclerPool中复用的ViewHolder需要重新绑定数据。

 mCachedViews是离屏缓存,用于缓存指定位置的 ViewHolder ,只有“列表回滚”这一种场景(刚滚出屏幕的表项再次进入屏幕),才有可能命中该缓存。该缓存存放在默认大小为 2 的ArrayList中。mCachedViews中复用的ViewHolder不需要重新绑定数据。

RecyclerView 缓存机制 | 回收些什么? - 掘金

RecyclerView 缓存机制 | 回收到哪去? - 掘金

mCachedViews有点像“回收池预备队列”,即总是先回收到mCachedViews,当它放不下的时候,按照先进先出原则将最先进入的ViewHolder存入回收池RecycledViewPool。

RecyclerView缓存机制 | scrap view 的生命周期 - 掘金

 mAttachedScrap生命周期起始于RecyclerView布局开始,终止于RecyclerView布局结束。

  • Recycler有4个层次用于缓存ViewHolder对象,优先级从高到底依次为ArrayList<ViewHolder> mAttachedScrapArrayList<ViewHolder> mCachedViewsViewCacheExtension mViewCacheExtensionRecycledViewPool mRecyclerPool。如果四层缓存都未命中,则重新创建并绑定ViewHolder对象

  • 缓存性能:

    缓存重新创建ViewHolder重新绑定数据
    mAttachedScrapfalsefalse
    mCachedViewsfalsefalse
    mRecyclerPoolfalsetrue
  • 缓存容量:

    • mAttachedScrap:没有大小限制,但最多包含屏幕可见表项。
    • mCachedViews:默认大小限制为2,放不下时,按照先进先出原则将最先进入的ViewHolder存入回收池以腾出空间。
    • mRecyclerPool:对ViewHolderviewType分类存储(通过SparseArray),同类ViewHolder存储在默认大小为5的ArrayList中。
  • 缓存用途:

    • mAttachedScrap:用于布局过程中屏幕可见表项的回收和复用。
    • mCachedViews:用于移出屏幕表项的回收和复用,且只能用于指定位置的表项,有点像“回收池预备队列”,即总是先回收到mCachedViews,当它放不下的时候,按照先进先出原则将最先进入的ViewHolder存入回收池。
    • mRecyclerPool:用于移出屏幕表项的回收和复用,且只能用于指定viewType的表项
  • 缓存结构:

    • mAttachedScrapArrayList<ViewHolder>
    • mCachedViewsArrayList<ViewHolder>
    • mRecyclerPool:对ViewHolderviewType分类存储在SparseArray<ScrapData>中,同类ViewHolder存储在ScrapData中的ArrayList中。

读源码长知识 | 更好的 RecyclerView 表项点击监听器 - 掘金

策略模式应用 | 每当为 RecyclerView 新增类型时就很抓狂 - 掘金

更好的 RecyclerView 表项子控件点击监听器 - 掘金

更高效地刷新 RecyclerView | DiffUtil二次封装 - 掘金

换一个思路,超简单的RecyclerView预加载 - 掘金

RecyclerView 动画原理 | 换个姿势看源码(pre-layout) - 掘金

RecyclerView 动画原理 | pre-layout,post-layout 与 scrap 缓存的关系 - 掘金

  • RecyclerView为了实现表项动画,进行了 2 次布局(预布局 + 后布局),在源码上表现为LayoutManager.onLayoutChildren()被调用 2 次。

  • State.mInPreLayout用于标识是否在预布局阶段。预布局的生命周期始于RecyclerView.dispatchLayoutStep1(),终于RecyclerView.dispatchLayoutStep2()

  • 在预布局阶段,循环填充表项时,若遇到被移除的表项,则会忽略它占用的空间,多余空间被用来加载额外的表项,这些表项在屏幕之外,本来不会被加载。

RecyclerView 动画原理 | 如何存储并应用动画属性值? - 掘金

  • RecyclerView 将表项动画数据封装了两层,依次是ItemHolderInfoInfoRecord,它们记录了列表预布局和后布局表项的位置信息,即表项矩形区域与列表左上角的相对位置,它还用一个int类型的标志位来记录表项经历了哪些布局阶段,以判断表项应该做的动画类型(出现,消失,保持)。

  • InfoRecord被集中存放在一个商店类ViewInfoStore中。所有参与动画的表项的ViewHolderInfoRecord都会以键值对的形式存储其中。
  • RecyclerView 在布局的第三阶段会遍历商店类中所有的键值对,以InfoRecord中的标志位为依据,判断执行哪种动画。表项预布局和后布局的位置信息会一并传递给RecyclerView.ItemAnimator,以触发动画。
  • RecyclerView.ItemAnimator收到动画指令和数据后,又将他们封装为MoveInfo,不同类型的动画被存储在不同的MoveInfo列表中。然后将执行动画的逻辑抛到 Choreographer 的动画队列中,当下一个垂直同步信号到来时,Choreographer 从动画队列中取出并执行表项动画,执行动画即遍历所有的MoveInfo列表,为每一个MoveInfo构建 ViewPropertyAnimator 实例并启动动画。

 RecyclerView 面试题 | 滚动时表项是如何被填充或回收的? - 掘金

  • RecyclerView 在滚动发生之前,会根据预计滚动位移大小来决定需要向列表中填充多少新的表项。

  • RecyclerView 填充表项是通过while循环一个一个实现的,当列表没有剩余空间时,填充表项也就结束了。

  • RecyclerView 滑动发生之前,会计算出一条limit 隐形线,它是决定哪些表项该被回收的重要依据。它可以理解为:隐形线当前所在位置,在滚动完成后会和列表顶部重叠

  • limit 隐形线的初始值 = 列表当前可见表项的底部到列表底部的距离,即列表在不填充新表项时,可以滑动的最大距离。每一个新填充表项消耗的像素值都会被追加到 limit 值之上,即limit 隐形线会随着新表项的填充而不断地下移。

  • 触发回收逻辑时,会遍历当前所有表项,若某表项的底部位于limit 隐形线下方,则该表项上方的所有表项都会被回收。

RecyclerView 面试题 | 哪些情况下表项会被回收到缓存池? - 掘金

RecyclerView 性能优化 | 把加载表项耗时减半 (一) - 掘金

Window.addOnFrameMetricsAvailableListener()方法可以监听最近 120 帧的绘制耗时。

RecyclerView 性能优化 | 把加载表项耗时减半 (二) - 掘金

RecyclerView 性能优化 | 把加载表项耗时减半 (三) - 掘金

RecyclerView 的滚动是怎么实现的?(一)| 解锁阅读源码新姿势 - 掘金

RecyclerView 的滚动时怎么实现的?(二)| Fling - 掘金

 RecyclerView 刷新列表数据的 notifyDataSetChanged() 为什么是昂贵的? - 掘金

 

优化嵌套的RecyclerView

可以给RecyclerVIew设置自定义的视图池,代码看起来像这样: 

public OuterRecyclerViewAdapter(List<Item> items) {
    //Constructor stuff
    viewPool = new RecyclerView.RecycledViewPool();
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    //Create viewHolder etc
    holder.innerRecyclerView.setRecycledViewPool(viewPool);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值