手摸手第二弹,可视化 RecyclerView 缓存机制

欢迎关注本人公众号,扫描下方二维码或搜索公众号 id: mxszgggit

前言

开题前,笔者仍是要说几句先,依旧和前文同样,文章内不涉及源码讲解,默认各位读者对源码有必定的了解,撰文的缘由也如同前文,由于笔者认为当下在 ListView/RecyclerView 的源码讲解的文章中,大都是对着源码噼里啪啦,实在有些晦涩难懂,因而笔者想将部分数据可视化,手摸手带领读者去了解一下缓存机制的实现,另推荐阅读腾讯 Bugly 的《Android ListView与RecyclerView对比浅析--缓存机制》一文。github

1.前文地址:《可视化 ListView 缓存机制,手摸手带你打通任督二脉》算法

2.本文项目地址:RecyclerViewVisualization 或直接下载 apksegmentfault

但愿阅读本文前请先阅读前文,本文所涉及的一些关键字在上文有所说起。数组

一缓

手摸手打开 app:缓存

这里写图片描述

RecyclerView 中的一缓 mAttachedScrap 与 ListView 中的一缓 mActiveViews 功能是基本类似的,为了屏幕内 item 快速复用而存在(RecyclerView/ListView 具备两次 onLayout() 过程,第二次 onLayout() 中直接使用第一次 onLayout() 缓存的 View,而没必要再建立)。数据结构

二缓

实际上,二缓 mCachedViews 加上四缓 RecyclerViewPool 合在一块儿与 ListView 的二缓 mScrapedViews 意义相同,为了即将给即将入屏的 item 复用而存在。下面来细谈下二缓:app

  • ArrayList 类型
  • 默认 size 为 2
  • size 可变
  • 复用算法是从尾部倒序匹配 ViewHolder position 与传入的 position 是否相等,匹配成功则返回
  • 为了优化上一步,下一个可能出现的 item 将会被置于尾部

二缓是经过 position 来匹配相应的 ViewHolder 的,这里的 position 指的是 RecyclerView 预测的、可能进入屏幕的 item 的 position,它是由当前屏幕滑动方向和可见的 item 位置来共同决定的。例如:屏幕向下滑动,那么可能进入屏幕的 item 的 position 就是当前可见第一个 item 的 position - 1;屏幕向上滑动,那么可能进入屏幕的 item 的 position 就是当前可见的最后一个 item 的 position + 1。这样提及来可能有些模糊,举个例子:post

这里写图片描述

以上述状态来讲,若是屏幕下滑,那么预测下一个可能出如今屏幕上的 item 的 position 多是 4(也就是 Item1『E(layoutPosition:4)』);而若是屏幕上滑,预测的下一个出如今屏幕上的 item 的 position 是 0(也就是 Item『A(layoutPosition:0)』)。而后经过将 position 用于与 mCacheViews 中的 ViewHolder 的 layoutPosition 作比较,若是相同则返回该 ViewHolder。性能

来看动图:

1.屏幕上滑:

这里写图片描述

能够看到 target mCacheView position 由 0 变成了 4。与此同时,mCachedViews 将可能出如今屏幕上的 item 的位置从原有的位置调整为 ArrayList 的最后一位。

2.屏幕下滑:

这里写图片描述

屏幕下滑的话,target mCachedView position 由 4 变成了 0。与此同时,mCachedViews 内部也会作相应的调整。

四缓

四缓(RecycledViewPool)性质:

  • 内部维护了一个 SparseArray

  • SparseArray key 为 ViewHolder 的 ViewType,这说明每一套 ViewHolder 都具备本身的缓存数据

  • SparseArray value 为 ScrapData 类型,ScrapData 就是关键的缓存数据了,其数据结构简略以下:

    static class ScrapData {
          ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
          int mMaxScrap = 5;
          // ...
      }
    复制代码

因而可知,针对每一种 ViewHolder,RecycledViewPool 都会维护一个默认大小为 5 的 ArrayList 来用作缓存它们,固然,这里还须要说起的一点是,ArrayList 的默认大小被限制为 5,可是这个值是能够经过 RecycledViewPool#setMaxRecycledViews(viewType, max) 来替换的,好比想换成大一点的 十、20,都是能够的(这也是该数据类型为 ArrayList 而不是数组的缘由之一)。

前面说到——“实际上,二缓 mCachedViews 加上四缓 RecyclerViewPool 合在一块儿与 ListView 的二缓 mScrapedViews 意义相同,为了即将给即将入屏的 item 复用而存在。”,可能有小伙伴疑惑了,既然意义相同,为什么不是只有二缓就足够了,还要多一个四缓来更复杂?原因在于:能够由开发者主动向内填充数据(RecycledViewPool#putRecycledView(ViewHolder),技术上能够实现多个 RecyclerView 共用同一个 RecyclerViewPool(RecyclerView#setRecycledViewPool(RecycledViewPool))。这两点在笔者看来,是在某种业务场景下选择 RecyclerView 仍是 ListView 的一个重要原因所在。至于这两点的实践,第一点笔者已经添加在 Demo 中了(含彩蛋),第二点笔者就不在此处扩展了,各位读者能够自行添加入 RecyclerViewVisualization Demo 中,相应的数据也都会被展现到屏幕上~

其余

谈谈 BindView(与 ListView 对比)

一个 View 被完整的展现到屏幕上,应该通过建立 View 和给 View 添加数据(BindView)两个过程,因此实际上缓存机制不只仅针对于 View 要作缓存,最好还能对添加数据的这个过程再优化下,毕竟 setText()、setImage() 也多是一个耗时操做。RecyclerView 就针对此作了优化,咱们知道,ListView 实际上缓存的是 View,而 RecyclerView 实际上缓存的是 ViewHolder,这就意味着 ListView 虽然能够复用 View,可是给 View 添加数据这个过程就不能复用了,而若是是复用 ViewHolder 的话,不只复用了 View,同时将给 View 添加数据的这个过程也被“缓存”起来了,而 RecyclerView 就是这么干的 ——

这里写图片描述

咱们能够看到,但凡是被二缓缓存起来的 ViewHolder 再被展现到屏幕上,是不会触发 BindViewHolder 这个过程的。

ps:固然,这得基于数据源不变,若是数据源改变,确定得从新给 View 添加数据。

谈谈 BindView(局部刷新)

RecyclerView 相比于 ListView 还提供了局部刷新的接口,这让 RecyclerView 在性能上又有了一个亮点:

1.局部刷新:

这里写图片描述

2.全局刷新:

这里写图片描述

能够看到,局部刷新可以只针对改变的 View 进行 bind view,而全局刷新会针对被影响到的全部的 View 都进行 bind view。因此,在平常使用中,是否是应该多考虑使用局部刷新代替全局刷新呢?

相关文章
相关标签/搜索