RecycleView 复用之一

先说结论吧

RecycleView有4级缓存

1.Attached scrap & Changed scrap
屏幕中的缓存,用于数据刷新时,不需要重新加载子ItemView,直接复用
2.mCachedViews
刚刚移出屏幕的缓存,最大容量为2(可以考虑自行修改),通过position来保存,数据不变,直接复用;滑动时,该缓存一边add,一边remove.后面会解释
3.mViewCacheExtension自定义缓存,没用过
4.mRecyclerPool保存mCachedViews缓存中保存不了的ItemView。通过itemType来保存,每种itemType可以保存5个ItemView,可以通过RecycleView#recycledViewPool.setMaxRecycledViews(int max)来修改,但是会增加内存开销,如果当前缓存没有找到对应itemType的ViewHolder,那么会重新走onCreateViewHolder以及onBindViewHolder
以上如果有命中缓存,只会执行onBindViewHolder

RecycleView滑动过程一定能命中缓存吗?

不一定,我们知道,命中缓存的关键,主要依靠mCachedViews(默认2个)跟mRecyclerPool(默认5个)。如果说,当前的数据viewType是按顺序,依据类型添加的。那么肯定可以进行缓存复用。代码如下:

       for(i in 0 until 20){
            list.add(MyVhDataList(1, "holderA$i"))
        }
        for (i in 0 until 20){
            list.add(MyVhDataList(2, "holderB$i"))
        }

从list数据结构可以看出,我们一次添加了viewType为1的数据20个,viewType为2的数据20个。根据缓存的规则,我们任意上下滑动,都会对当前的viewType对应的mRecyclerPool进行赋值(最大长度为5),并从mRecyclerPool取出对应viewType所对应的holder对象,进行复用。简单说就是不会从小走onCreateViewHolder,只会执行onBindViewHolder

但是!!!如果数据类型如下:

 for(i in 0 until 20){
            list.add(MyVhDataList(1, "holderA$i"))
        }
        for (i in 0 until 20){
            list.add(MyVhDataList(2, "holderB$i"))
        }
        for (i in 0 until 20){
            list.add(MyVhDataList(3, "holderC$i"))
        }
        for(i in 0 until 20){
            list.add(MyVhDataList(1, "holderA$i"))
        }

从数据初始化,我们可以看出,我们依次添加了viewType为1的holder20个,viewType为2的holder20个,viewType为3的holder20个,最后,又添加viewType为1的holder20个。继续根据回收复用原理,来理解。

1.假设,我首次渲染数据,一屏可以显示6个数据,加上预加载的2个数据(这里暂时不分析预加载)。那么我会执行8次onCreateViewHolder。当我持续往上滑动的时候,又会执行3次onCreateViewHolder,总共执行11次onCreateViewHolder。后续就是直接复用已创建好的这些ViewHolder。
源码Recycleview#tryGetViewHolderForPositionByDeadline从这里来分析,获取复用数据

注意!!!!这里首先是上滑!上滑!上滑!上滑!上滑!
1.把屏幕前2个item滑出当前屏幕区域会直接放入mCachedViews,
2.把第3个item滑动屏幕,会把当前viewHolder添加(add)到mCachedViews,然后判断mCachedViews.size,
如果超过2个,就remove第一个,并准备放入mRecyclerPool,进而判断mRecyclerPool内部对应viewType的scrapHeap.size,大于5直接返回,否则,放出mRecyclerPool。
3.后续,每个viewType为1的item滑出屏幕后,都会执行第二步操作。
这里着重记住一点。mCachedViews的复用,只会判断position,所以,我们这里只对pool进行分析。考虑到每次item滑出屏幕,就一定有item滑进屏幕。所以复用的顺序是,先addViewHolderToRecycledViewPool,再执行getRecycledView#scrapHeap.remove(i)
4.也就是说,当我们把前面2个item滑出屏幕的时候,都会去找能否有复用的holder,
找不到就会执行onCreateViewHolder。这里执行了2次。
到目前为止,总共执行了10次onCreateViewHolder。
这个时候mCachedViews,保存了两个holder,mRecyclerPool保存了0个holder。
当我们把第三个item滑出屏幕的时候,会先去mCachedViews找能否复用的holder,这个时候是找不到的。
所以又去mRecyclerPool找,这个时候也是找不到的。
于是执行了第11次onCreateViewHolder。
执行第二步,最后,mCachedViews,总共有2个holder。
mRecyclerPool有一个holder。
5.当我们把第4个item滑出屏幕的时候,执行第二步,先把holder放入mCachedViews,由于mCachedViews.size>2。所以,会把mCachedViews[0]。放入mRecyclerPool。此时mRecyclerPool里面有两个holder,由于在mCachedViews找不到对应的holder复用。
于是,继续去mRecyclerPool找,这个时候,找到对应viewType存放的1个holder,取出进行复用。并删除当前mRecyclerPool存放的viewType。
6.后续,就是重复以上第5步骤
4.综上:有2个item对应的ViewHolder放入mCachedViews,有2个放入mRecyclerPool,根据我们上面说的,
总共会执行11次onCreateViewHolder,

以上分析的是上滑,对mCachedViews以及mRecyclerPool进行赋值
以下分析的是下滑!下滑!!下滑!!下滑!!

下滑其实跟上面讲的第5个步骤一样。。。。。

这里说重点了,为毛上述的数据结构,滑动的时候还会继续执行onCreateViewHolder,也就是为毛找不到可以复用的holder。

1.根据上述分析,当我们把第一个type==1的holder全部划走之后,由于底下是type==2、3的holder。所以mRecyclerPool会一直存放viewType为1的holder,并不会复用取出,mRecyclerPool对应viewType==1最多有5个
当我们继续滑动其他类型的holder的时候,mRecyclerPool存放的是viewType为2、3的holder,当我们继续上滑到另外一组viewType为1的时候,由于mRecyclerPool存放的viewType为1的holder有5个,当取出复用后,并没有viewType为1的holder继续存放到mRecyclerPool。所以,当我们滑动的过程中,只有一部分viewType为1的holder能被找到并复用。直到,屏幕中全部都是viewType为1的holder后,重新执行回收复用流程,才能继续复用。

结论

以上就是整个Recycleview复用holder的流程。有点啰嗦。尽量不讲源码,用案例来解释。总之一定注意,如果为了复用的可行性以及高效性。尽量一定要显示ABC这种数据类型。如果有ABCA这种情况。一部分item的复用就失效了。
这里补充一句,有些业务都是使用ScrollView嵌套Recycleview的形式。这种也会让复用失效。
解决方案,参考
解决NestedScrollView与RecyclerView嵌套,导致RecyclerView无法复用,滑动冲突等问题
另外还有一种业务涉及到垂直Recycleview嵌套item水平Recycleview。这种情况水平的Recycleview的复用也会失效,暂时没有去研究。
优化方案,参考,直接看第六点吧。
共享RecycledViewPool
说实话,RecycleviewView的设计简直牛逼。但是,使用不规范,确实很难排查并优化了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容