RecyclerView缓存原理及优化方向

前沿

Android新增的Recyclerview主要用于代替ListView。Recyclerview可扩展性强。

  • 可以通过LayoutManager形成线性(横向与竖向)、网格、瀑布流布局。
  • 通过OnItemTouchListener监听 Item 的事件,虽然比ListView.OnItemClickListener麻烦了点,但是可以实现更复杂的功能,比如item滑动。
  • 提供了notifyItemInserted、notifyItemRemoved、notifyItemChanged、notifyItemMoved来提高局部刷新的效率。
  • 没有ListView那种 HeaderView 和 FooterView , 但可以通过来 getItemViewType来生成不同的视图。
  • RecyclerView还定义了ViewHolder,配合RecyclerView.Adapter,封装重用ItemView的逻辑,还有四级缓存,效率大大增加。

1. RecyclerView缓存机制与性能优化关系

RecyclerView做性能优化要说复杂也复杂,比如说布局优化,缓存,预加载等等。

其优化的点很多,在这些看似独立的点之间,其实存在一个枢纽:Adapter。

因为所有的ViewHolder的创建和内容的绑定都需要经过Adaper的两个函数onCreateViewHolder和onBindViewHolder

因此我们性能优化的本质就是要**减少这两个函数的调用时间和调用的次数**

如果我们想对RecyclerView做性能优化,必须清楚的了解到我们的每一步操作背后,onCreateViewHolder和onBindViewHolder调用了多少次。

因此,了解RecyclerView的缓存机制是RecyclerView性能优化的基础。


1655326cbdee046f.png

2. 绘制原理简述

2.1 假设

为了简化问题,绘制原理介绍提供以下假设:

  • RecyclerView
    • 以LinearLayoutManager为例
    • 忽略ItemDecoration
    • 忽略ItemAnimator
    • 忽略Measure过程
    • 假设RecyclerView的width和height是确定的
  • Recycler
    • 忽略mViewCacheExtension

2.2 绘制过程

(1)类的职责介绍
LayoutManager:接管RecyclerView的Measure,Layout,Draw的过程

Recycler:缓存池

Adapter:ViewHolder的生成器和内容绑定器。

(2)绘制过程简介

  • 1、RecyclerView.requestLayout开始发生绘制,忽略Measure的过程
  • 2、在Layout的过程会通过LayoutManager.fill去将RecyclerView填满
  • 3、LayoutManager.fill会调用LayoutManager.layoutChunk去生成一个具体的ViewHolder
  • 4、然后LayoutManager就会调用Recycler.getViewForPosition向Recycler去要ViewHolder
  • 5、Recycler首先去一级缓存(Cache)里面查找是否命中,如果命中直接返回。如果一级缓存没有找到,则去三级缓存查找,如果三级缓存找到了则调用Adapter.bindViewHolder来绑定内容,然后返回。如果三级缓存没有找到,那么就通过Adapter.createViewHolder创建一个ViewHolder,然后调用Adapter.bindViewHolder绑定其内容,然后返回为Recycler。
  • 6、一直重复步骤3-5,知道创建的ViewHolder填满了整个RecyclerView为止。


    1655326cbddd7d9e.png

3. 缓存机制

3.1 Recyclerview的缓存类

RecyclerView缓存基本上是通过三个内部类管理的,RecyclerRecycledViewPoolViewCacheExtension
Recycler
用于管理已经废弃或者与RecyclerView分离的ViewHolder,为了方便理解这个类,整理了下面的资料,内部类的成员变量和他们的含义:

变量 作用
mChangedScrap 与RecyclerView分离的ViewHolder列表
mAttachedScrap 未与RecyclerView分离的ViewHolder列表
mCachedViews ViewHolder缓存列表
mViewCacheExtension 开发者可以控制的ViewHolder缓存的帮助类
mRecyclerPool ViewHolder缓存池

RecycledViewPool
RecycledViewPool类是用来缓存Item用,是一个ViewHolder的缓存池,如果多个RecyclerView之间用setRecycledViewPool(RecycledViewPool)设置同一个RecycledViewPool,他们就可以共享Item。
其实RecycledViewPool的内部维护了一个Map,里面以不同的viewType为Key存储了各自对应的ViewHolder集合。可以通过提供的方法来修改内部缓存的Viewholder。
ViewCacheExtension
开发者可自定义的一层缓存,是虚拟类ViewCacheExtension的一个实例,开发者可实现方法getViewForPositionAndType(Recycler recycler, int position, int type)来实现自己的缓存。

3.2 Recyclerview的四级缓存

3.2.1 屏幕内缓存

屏幕内缓存指在屏幕中显示的ViewHolder,这些ViewHolder会缓存在mAttachedScrap、mChangedScrap中 :

  • mChangedScrap 表示数据已经改变的ewHolder列表
    表示数据已经改变的viewHolder列表,存储 notifXXX 方法时需要改变的 ViewHolder,匹配机制按照position和id进行匹配
  • mAttachedScrap 未与RecyclerView分离的ViewHolder列表
    未与RecyclerView分离的ViewHolder列表,如果仍依赖于 RecyclerView (比如已经滑动出可视范围,但还没有被移除掉),但已经被标记移除的 ItemView 集合会被添加到 mAttachedScrap 中。

3.2.2 屏幕外缓存

当列表滑动出了屏幕时,ViewHolder会被缓存在 mCachedViews ,其大小由mViewCacheMax决定,默认DEFAULT_CACHE_SIZE为2,可通过Recyclerview.setItemViewCacheSize()动态设置。

3.2.3 自定义缓存

可以自己实现ViewCacheExtension类实现自定义缓存,可通过Recyclerview.setViewCacheExtension()设置。

  • 适用场景:android.jlelse.eu/anatomy-of-…

  • 位置固定

  • 内容不变

  • 数量有限

  • 三级缓存:返回View

    • 按照position和type进行匹配
    • 直接返回View
    • 需要自己继承ViewCacheExtension实现
    • 位置固定,内容不发生改变的情况,比如说Header如果内容固定,就可以使用

3.2.4 缓存池

ViewHolder在首先会缓存在 mCachedViews 中,当超过了个数(比如默认为2), 就会添加到 RecycledViewPool 中。
在有限的mCachedViews中如果存不下ViewHolder时,就会把ViewHolder存入RecyclerViewPool中。
* 按照Type来查找ViewHolder
* 每个Type默认最多缓存5个

RecycledViewPool 会根据每个ViewType把ViewHolder分别存储在不同的列表中,每个ViewType最多缓存DEFAULT_MAX_SCRAP = 5 个ViewHolder,如果RecycledViewPool没有被多个RecycledView共享,对于线性布局,每个ViewType最多只有一个缓存,如果是网格有多少行就缓存多少个。


1155837-c10b3fce7c974aee.png
  • 四级缓存:返回布局有效,内容无效的ViewHolder
    • 按照type进行匹配,每个type缓存值默认=5
    • layout是有效的,但是内容是无效的
    • 多个RecycleView可共享,可用于多个RecyclerView的优化

3.3 缓存策略

Recyclerview在获取ViewHolder时按四级缓存的顺序查找,如果没找到就创建。其中只有RecycledViewPool找到时才会调用 bindViewHolder,其它缓存不会重新bindViewHolder 。

1155837-e5365d4a8d217428.png

3.4 总结

通过了解RecyclerView的四级缓存,我们可以知道,RecyclerView最多可以缓存 N(屏幕最多可显示的item数) + 2 (屏幕外的缓存) + 5*M (M代表M个ViewType,缓存池的缓存),只有RecycledViewPool找到时才会重新调用 bindViewHolder。

RecyclerView在Recyler里面实现ViewHolder的缓存,Recycler里面的实现缓存的主要包含以下5个对象:

  • ArrayList mAttachedScrap:

    • 按照id和position来查找ViewHolder
  • ArrayList mChangedScrap:

  • ArrayList mCachedViews:缓存ViewHolder,主要用于解决RecyclerView滑动抖动时的情况,还有用于保存Prefetch的ViewHoder

    • 最大的数量为:mViewCacheMax = mRequestedCacheMax + extraCache(extraCache是由prefetch的时候计算出来的)
  • ViewCacheExtension mViewCacheExtension:开发者可自定义的一层缓存,是虚拟类ViewCacheExtension的一个实例,开发者可实现方法getViewForPositionAndType(Recycler recycler, int position, int type)来实现自己的缓存。

  • mRecyclerPool ViewHolder缓存池,

3.2 缓存机制图解

RecyclerView在设计的时候讲上述5个缓存对象分为了3级。
每次创建ViewHolder的时候,会按照优先级依次查询缓存创建ViewHolder
三级缓存分别是:

  • 一级缓存:返回布局和内容都都有效的ViewHolder
    • 按照position或者id进行匹配
    • 命中一级缓存无需onCreateViewHolder和onBindViewHolder
    • mAttachScrap在adapter.notifyXxx的时候用到
    • mChanedScarp在每次View绘制的时候用到,因为getViewHolderForPosition非调用多次,后面将
    • mCachedView:用来解决滑动抖动的情况,默认值为2

3. RecyclerView性能优化方向总结

1655326cbe18dc01.png

参考

RecyclerView缓存原理,有图有真相
关于Recyclerview的缓存机制的理解

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