RecyclerView 回收复用

RecyclerView的缓存分为四级
Scrap
Cache
ViewCacheExtension
RecycledViewPool

1.Scrap对应ListView 的Active View,就是屏幕内的缓存数据,就是相当于换了个名字,可以直接拿来复用。(感觉就是临时存储,因为源码中貌似没有使用到)
2.Cache 刚刚移出屏幕的缓存数据,默认大小是2个,当其容量被充满同时又有新的数据添加的时候,会根据FIFO原则,把优先进入的缓存数据移出并放到下一级缓存中,然后再把新的数据添加进来。Cache里面的数据是干净的,也就是携带了原来的ViewHolder的所有数据信息,数据可以直接来拿来复用。需要注意的是,cache是根据position来寻找数据的,这个postion是根据第一个或者最后一个可见的item的position以及用户操作行为(上拉还是下拉)。
举个栗子:当前屏幕内第一个可见的item的position是1,用户进行了一个下拉操作,那么当前预测的position就相当于(1-1=0),也就是position=0的那个item要被拉回到屏幕,此时RecyclerView就从Cache里面找position=0的数据,如果找到了就直接拿来复用。
3.ViewCacheExtension是google留给开发者自己来自定义缓存的,这个ViewCacheExtension我个人建议还是要慎用,因为我扒拉扒拉网上其他的博客,没有找到对应的使用场景,而且这个类的api设计的也有些奇怪,只有一个public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position, int type);让开发者重写通过position和type拿到ViewHolder的方法,却没有提供如何产生ViewHolder或者管理ViewHolder的方法,给人一种只出不进的赶脚,还是那句话慎用。
4.RecycledViewPool刚才说了Cache默认的缓存数量是2个,当Cache缓存满了以后会根据FIFO(先进先出)的规则把Cache先缓存进去的ViewHolder移出并缓存到RecycledViewPool中,RecycledViewPool默认的缓存数量是5个。RecycledViewPool与Cache相比不同的是,从Cache里面移出的ViewHolder再存入RecycledViewPool之前ViewHolder的数据会被全部重置,相当于一个新的ViewHolder,而且Cache是根据position来获取ViewHolder,而RecycledViewPool是根据itemType获取的,如果没有重写getItemType()方法,itemType就是默认的。因为RecycledViewPool缓存的ViewHolder是全新的,所以取出来的时候需要走onBindViewHolder()方法。
再来张图看看整体流程


总结:
viewCache:高速缓存,可以拿来直接使用,不需要重新绑定数据。默认容量是2. 查找的规则是根据 position ,和id ,所以经常是差不到,基本上只有2个滑动的方向是相反的时候,会起到作用。
pool:缓存池,viewHolder 在进入pool 池前会先清除数据,所有复用这里的viewHolder 需要执行 onBindViewHolder()进行数据绑定。查询规则是 itemType,是一个 SparseArray保存的(SparseArray 类似于HashMap) ,key 是itemType ,pool 的数量是按照itemType 来区分的,每种类型默认保存5个。
viewCache (高速缓存),虽然是先去这里找但是这个找的方式是通过position 来找的,一定会去找,而不是说一定会找到。之前理解有误,以为一定会用到

例如:
现在总共有10调条数据,屏幕能显示5条(每行只有一条数据)
首次加载: onCreateViewHolder 会调用5次,全部数据都是新create ,这个时候 高速缓存,缓存池中都是 空的
向上滑动屏幕,加载新一条数据,这个时候缓存全是空,只能重新创建,这个时候position为0的数据会进入 高速缓存,高速缓存的容量,这个时候再次向上滑动加载新的一条数据,首先去高速缓存找,虽然高速缓存中有,但是因为position 不同,高速缓存里面存入的是position=0的数据,而我们新增的是position=6条数据,这个时候 pool中也没有可复用的,只能create ,position=1的数据进入高速缓存,(现在屏幕是2-6,高速缓存是0-1,pool 中没有)再次滑动,加载position=7数据,重复上面的过程,高速缓存中没position 为7的数据 而pool 中也没有缓存,所以继续create,这个时候position =2的数据进入高速缓存,position 为1 的数据进入 pool 中,同时数据被清空,即posititon 为1的数据已经不存在了(屏幕3-7,高速缓存 1-2,pool 中是0),pool 中此时有一个 viewHolder 但是没数据。再次滑动加载position=8数据,首先进入高速缓存,发现没有position =8的数据,没有找到,进入pool 中找,发现有ViewHolder 但是没有数据,这个时候不在create,因为 viewHolder 已有,只需要onbindViewHolder()重新绑定数据即可。
后续的滑动则不会再create 新的ViewHolder 了。所以供给创建了8 个ViewHolder 后续的操作就是如果高速缓存能够复用 则直接复用,不能复用则去Pool 中获取ViewHolder 对象,然后重新绑定数据。
情况2:
每行有5列,满屏是2行。相当于是屏幕默认的容量是10,与上面不同的就是每次加载量。
第一次向工滑动,这个时候缓存都为空,新建5个,第一行退出屏幕,这个时候屏幕是(5-9)高速缓存里是(0-1),pool 中是(2-4),已经创建了15次,这个时候继续向下滑动,首先去高速缓存里找,没有能找到对应position 的,继续pool 中找,发现有3个能复用,但是需要5个所以只能新建2个,到这里已经新建17个了。现在这个时候第2行的前两个数据(5-6)进入高速缓存,高速缓存中原有的数据和第二行剩下的3个数据进入pool 中,这个时候屏幕,高速缓存,poll 全部都填满了。这个时候在向下滑动的时候,就不会在重建了,因为即使高速缓存中找不到数据那pool 中一定是有足够数量的ViewHolder 的,只需要重新绑定数据即可。



(图片来源网络)
根据打印的log 通过 onBindViewHolder 的复用 positon 看到每一列在退出屏幕的时候进入高速缓存的是前2个,而非最后2个。
第三种情况就是 如果每列超过了5个
这个时候还是上面一样的机制,只不过这个时候pool 中最多有5个,所以如果每次向下滑动都需要新建2个吧,向上滑动如果告诉缓存中能被复用,则不需新建,否则应该也是要新建2个复用5个的。这种情况肯定是不好的嘛,所以尽可能不要超过五个.

总结:
一.复用与回收的顺序?
先复用在回收,道理很简单,如果是先回收,那高速缓存没意义了,因为如过时先回收,那高速缓存里的数据永远是刚刚退出屏幕的数据,不可能被服用到。
二.最多生成的viewHolder 数量?
分3种情况:
1.每行只有一列: 满屏数+高速缓存+1(因为每次只有新增1个,所以pooL中有一个备用足以)
2.每行是多列但是小于5 :屏幕数量+每一行的列数 +2,
3.如果每列大于5个的时候 :即使屏幕填满,高速缓存填满,pool 填满的话依然,仍然可能每次都要新建2个,除非高速缓存能复用。
三. 高速缓存可以设置数量,pool也可以自定义

补充:ListView
Listview的缓存机制
ListView的缓存有两级,在ListView里面有一个内部类 RecycleBin,RecycleBin有两个对象Active View和Scrap View来管理缓存,Active View是第一级,Scrap View是第二级。

Active View:是缓存在屏幕内的ItemView,当列表数据发生变化时,屏幕内的数据可以直接拿来复用,无须进行数据绑定。

Scrap view:缓存屏幕外的ItemView,这里所有的缓存的数据都是"脏的",也就是数据需要重新绑定,也就是说屏幕外的所有数据在进入屏幕的时候都要走一遍getView()方法。
再来一张图,看看ListView的缓存流程

ListView的缓存机制相对比较好理解,它只有两级缓存,一级缓存Active View是负责屏幕内的ItemView快速复用,而Scrap View是缓存屏幕外的数据,当该数据从屏幕外滑动到屏幕内的时候需要走一遍getView()方法。
生成的itemView 应该就是屏幕数量+每一行的列数,通常每行都是一个 就是 屏幕数+1,(满屏数量+1个缓存)当然如果是多列,或者多类型的就复杂了。比如一个多类型,第一次加载刚好显示的是类型1的数据,而开始向下滑动后,下一满屏都是另一个类型的,这个时候类型1应该全部进入缓存,数量就是满屏数量了吧。这个时候总共生成的数量反而比单一类型的少一个哈。

另外:滑动的时候一定是新的先出现哈,最顶部的退出一定是后于最底部新增的哈,只要top 让出一点距离,bottom 就进来了哈,只有当滑动停止的时候才会去判断边界,判断哪些该入缓存,这也是+1的原因而不是直接复用刚刚退出进入缓存的那个itemView。

参考:https://www.jianshu.com/p/3e9aa4bdaefd

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