【RecyclerView系列】浅析RecyclerView缓存机制之由来

1,054 阅读4分钟

前言

RecyclerView是Android开发工作中经常接触到的一个列表滑动控件,它由于良好的缓存复用机制使列表滚动非常丝滑。一般聊RecyclerView都离不开RecyclerView的缓存复用机制,但由于RecyclerView源码过多,阅读难度系数较高,所以我们可能都较少翻阅RecyclerView的源码。这篇文章暂不带大家看源码,先聊一下我个人对RecyclerView的缓存机制设计的一些理解,方便后续我们对RecyclerView每级缓存机制的理解,中间如有不妥之处还望不吝指出。

缓存机制的由来

我们试着从设计者角度去考虑,抛出以下几个问题:

  • 问题1:一个无限滑动的列表,在手机屏幕有限的空间里,每个itemView(包含不可见的)都要添加到RecyclerView上吗?

    A:答案当然是否定的,对于用户看不到的滑动出屏幕的itemView,我们想的肯定是从RecyclerView当中移除。

  • 问题2:那每次滑进屏幕一个ItemView,我们如果都去创建一个ItemView,并且绑定对应数据,这个合理吗?

    A:从问题1知道,我们把滑出屏幕的ItemView从RecyclerView中移除了,如果我们在移除的时候只是将ItemView从RecyclerView移除掉(removeView()),但却将这个ItemView缓存起来,每当有滑进屏幕的ItemView的时候,我们去缓存中去找有没有相同类型废弃的ItemView可用,如果有,则复用,重新绑定数据并添加到RecyclerView上即可。

  • 问题3:设想一个场景,我们在缓慢往上滑动RecyclerView,这个时候我们将ItemView1滑出了屏幕,然后突然我们手指停下来了, 我们有没有很大概率反向滑动再将ItemView1滑入到屏幕中,这个ItemView1 我们之前刚刚放到缓存中,我们是否还需要重新创建及绑定数据呢?

    A:我们想的方案当然是不需要再次创建ItemView,直接从缓存中取出来用,更优雅点的话,我希望我刚刚滑出去的view再次滑进来连绑定数据的步骤都省了,直接再次添加到RecyclerView上即可。

针对以上问题,我们再回想一下平时写的RecyclerView.Adapter 是如何写的:

  1. onCreateViewHolder(parent: ViewGroup, viewType: Int) 返回指定itemType类型的viewHolder

  2. onBindViewHolder(holder: ViewHolder, position: Int) 给你个viewHolder,通过position拿到对应position的数据,然后设置给viewHolder的itemView

怎么感觉我们一直都在和viewHolder打交道?瞬间有所顿悟,viewHolder是个啥东西?它除了持有了itemView的引用,它还有什么呢?

public abstract static class ViewHolder {
    @NonNull
    public final View itemView;//itemView的引用
   	...
    int mPosition = NO_POSITION;//位置属性
    ...
    int mItemViewType = INVALID_TYPE;//itemType
    ...
}

上面列出了一小部分我们常见的viewHolder的属性,发现它就像是itemView的持有者,同时还记录了当前这个itemView的很多附加属性:position、itemType、needUpdate、isInValid等等。发现和我们上面的所列出的问题不谋而合:

  • 针对问题1,我们可以缓存viewHolder
  • 针对问题2,我们通过itemType找到废弃的相同itemType的viewHolder复用,并重置ViewHolder的一些属性,比如position、needUpdate、isInvalid之类的,然后通过调用onBindViewHolder传入viewHolder、position重新绑定一下数据即可。
  • 针对问题3,我们可以同时通过itemType和position去匹配缓存中的viewHolder,如果找到那么我们可以认为这个viewHolder直接可以拿来用,将其itemView添加到RecyclerView上即可。

讲完这些,大家是不是对RecyclerView的通用缓存机制设计有点思路了。实际上,RecyclerView内部实现也基本如此,那上面说的缓存设计对应的是RecyclerView的哪些缓存集合呢?我们下篇文章再回答。

总结

这篇文章是为了让大家更好的理解缓存机制出现的原因:

  1. 无需频繁创建相同类型ViewHolder,以免过多内存占用或者频繁创建对象引起的内存抖动
  2. 我们希望可以通过复用废弃的相同itemType类型的ViewHolder重新绑定数据并添加到RecyclerView上即可
  3. 对于高频场景,我们可能连重新绑定数据的流程都想跳过,直接通过itemType和position拿到可复用的ViewHolder无需重新办定数据,直接添加到RecyclerView即可

想清楚缓存机制出现的原因及能够解决的问题后,对我们理解RecyclerView的缓存机制会很有帮助,也更容易看懂相关缓存设计了。当然有机会的话自己去实操一套类似的缓存机制,理解会更加清晰深刻。