RecyclerView 源码分析(三)RecyclerView的缓存机制

转载自琼珶和予

RecyclerView的缓存机制

RecyclerView作为一个非常惹人爱的控件,有一部分的功劳归于它优秀的缓存机制。RecyclerView的缓存机制属于RecyclerView的核心部分,同时也是比较难的部分。尽管缓存机制那么难,但是还是不能抵挡得住我们的好奇心。今天我们来看看它的神奇之处。

本文参考资料:
RecyclerView缓存原理,有图有真相
【进阶】RecyclerView源码解析(二)——缓存机制
深入 RecyclerView 源码探究四:回收复用和动画
手摸手第二弹,可视化 RecyclerView 缓存机制
RecyclerView 源码分析(一) - RecyclerView的三大流程
  
由于本文跟本系列的前两篇文章都有关联,所以为了便于理解,可以去看作者本系列的前两篇文章。
  
注意,本文所有的代码都来自于27.1.1。

1. 概述

在正式分析源码之前,我先对缓存机制做一个概述,同时也会对一些概念进行统一解释,这些对后面的分析有很大的帮助,因为如果不理解这些概念的话,后面容易看得雨里雾里的。

(1) 四级缓存

首先,我将RecyclerView的缓存分为四级,可能有的人将它分为三级,这些看个人的理解。这里统一说明一下每级缓存的意思。

缓存级别 实际变量 含义
一级缓存 mAttachedScrap和mChangedScrap 这是优先级最高的缓存,RecyclerView在获取ViewHolder时,优先会到这两个缓存来找。其中mAttachedScrap存储的是当前还在屏幕中的ViewHolder,mChangedScrap存储的是数据被更新的ViewHolder,比如说调用了Adapter的notifyItemChanged方法。可能有人对这两个缓存还是有点疑惑,不要急,待会会详细的解释。
二级缓存 mCachedViews 默认大小为2,通常用来存储预取的ViewHolder,同时在回收ViewHolder时,也会可能存储一部分的ViewHolder,这部分的ViewHolder通常来说,意义跟一级缓存差不多
三级缓存 ViewCacheExtension 自定义缓存,通常用不到,在本文中先忽略
四级缓存 RecyclerViewPool 根据ViewType来缓存ViewHolder,每个ViewType的数组大小为5,可以动态的改变

如上表,统一的解释了每个缓存的含义和作用。在这里,我再来对其中的几个缓存做一个详细的解释。

  1. mAttachedScrap:上表中说,它表示存储的是当前还在屏幕中ViewHolder实际上是从屏幕上分离出来的ViewHolder,但是又即将添加到屏幕上去的ViewHolder。比如说,RecyclerView上下滑动,滑出一个新的Item,此时会重新调用LayoutManager的onLayoutChildren方法,从而会将屏幕上所有的ViewHolder先scrap掉(含义就是废弃掉),添加到mAttachedScrap里面去,然后在重新布局每个ItemView时,会从优先mAttachedScrap里面获取,这样效率就会非常的高。这个过程不会重新onBindViewHolder
  2. mCachedViews默认大小为2,不过通常是3,3由默认的大小2 + 预取的个数1。所以在RecyclerView在首次加载时,mCachedViews的size为3(这里以LinearLayoutManager的垂直布局为例)。通常来说,可以通过RecyclerView的setItemViewCacheSize方法设置大小,但是这个不包括预取大小;预取大小通过LayoutManager的setItemPrefetchEnabled方法来控制

(2) ViewHolder的几个状态值

我们在看RecyclerView的源码时,可能到处都能看到调用ViewHolder的isInvalid、isRemoved、isBound、isTmpDetached、isScrap和isUpdated这几个方法。这里我统一的解释一下。

方法名 对应的Flag 含义或者状态设置的时机
isInvalid FLAG_INVALID 表示当前ViewHolder是否已经失效。通常来说,在3种情况下会出现这种情况:1.调用了Adapter的notifyDataSetChanged方法;2. 手动调用RecyclerView的invalidateItemDecorations方法;3. 调用RecyclerView的setAdapter方法或者swapAdapter方法
isRemoved FLAG_REMOVED 表示当前的ViewHolder是否被移除。通常来说,数据源被移除了部分数据,然后调用Adapter的notifyItemRemoved方法
isBound FLAG_BOUND 表示当前ViewHolder是否已经调用了onBindViewHolder
isTmpDetached FLAG_TMP_DETACHED 表示当前的ItemView是否从RecyclerView(即父View)detach掉。通常来说有两种情况下会出现这种情况:1.手动了RecyclerView的detachView相关方法;2. 在从mHideViews里面获取ViewHolder,会先detach掉这个ViewHolder关联的ItemView。这里又多出来一个mHideViews,待会我会详细的解释它是什么。
isScrap 无Flag来表示该状态,用mScrapContainer是否为null来判断 表示是否在mAttachedScrap或者mChangedScrap数组里面,进而表示当前ViewHolder是否被废弃
isUpdated FLAG_UPDATE 表示当前ViewHolder是否已经更新。通常来说,在3种情况下会出现情况:1.isInvalid方法存在的三种情况;2.调用了Adapter的onBindViewHolder方法;3. 调用了Adapter的notifyItemChanged方法

(3) ChildHelper的mHiddenViews

在四级缓存中,我们并没有将mHiddenViews算入其中。因为mHiddenViews只在动画期间才会有元素,当动画结束了,自然就清空了。所以mHiddenViews并不算入4级缓存中。
  
这里还有一个问题,就是上面在解释mChangedScrap时,也在说,当调用Adapter的notifyItemChanged方法,会将更新了的ViewHolder反放入mChangedScrap数组里面。那到底是放入mChangedScrap还是mHiddenViews呢?同时可能有人对mChangedScrap和mAttachedScrap有疑问,这里我做一个统一的解释:

首先,如果调用了Adapter的notifyItemChanged方法,会重新回调到LayoutManager的onLayoutChildren方法里面,而在onLayoutChildren方法里面,会将屏幕上所有的ViewHolder回收到mAttachedScrap和mChangedScrap

这个过程就是将ViewHolder分别放到mAttachedScrap和mChangedScrap,而什么条件下放在mAttachedScrap,什么条件放在mChangedScrap,这个就是他们俩的区别。

接下来我们来看一段代码,就能分清mAttachedScrap和mChangedScrap的区别了

void scrapView(View view) {
   
    final ViewHolder holder = getChildViewHolderInt(view);
    if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
            || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
   
        if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
   
            throw new IllegalArgumentException("Called scrap view with an invalid view."
                    + " Invalid views cannot be reused from scrap, they should rebound from"
                    + " recycler pool." 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值