RecyclerView 核心要点

首先感谢Jiaheng的分享,主要以记录内容为目的

主要记录内容包括:

  • RecyclerView和ListView优缺点
  • ViewHolder究竟是什么?
  • RecyclerView和Listview的缓存机制对比
  • 你可能不知道的RecyclerView 性能优化策略

RecyclerView和ListView优缺点

ListView的局限

  • 只有纵向列表一种布局
  • 没有支持动画的API
  • 没有强制实现ViewHolder
  • 性能不如RecyclerView

RecyclerView的优势

  • 默认支持 Linear, Grid, Staggered Grid 三种布局
  • 友好的 ItemAnimator 动画 API
  • 强制实现 ViewHolder
  • 解耦的架构设计
  • 相比 ListView 性能更好

ViewHolder究竟是什么?

  • ViewHolder 和 item view 是什么关系?一对一?一对多?多对一?
    以ListView为例:
    不使用ViewHolder
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        if (convertView == null) {
            convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_xxx, null)
        }
        var itemText = convertView!!.findViewById<View>(R.id.textView) as TextView
        itemText?.text = getItem(position)
        return convertView
    }

使用ViewHolder

  override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        var convertView = convertView
        val holder: ViewHolder
        if (null == convertView) {
            holder = ViewHolder()
            convertView = LayoutInflater.from(parent.context).inflate(R.layout.item_xxx, null)
            holder.itemText = convertView!!.findViewById<View>(R.id.textView) as TextView
            convertView.tag = holder
        } else {
            holder = convertView.tag as ViewHolder
        }
        holder.itemText?.text = getItem(position)
        return convertView
    }

在getView()方法中,使用不使用ViewHolder,都有判断convertView==null,所以控件都有复用,主要的区别在于不使用ViewHolder每次显示Item时,View都回去findViewById,不停的滚动就会一直查找控件。

image.png

如图所示,其实item view 和 ViewHolder 是 一一对应的关系,因为每创建一个item view 需要创建一个ViewHolder,然后后面需要使用ViewHolder的时候是从convertView中getTag中取出ViewHolder来使用。所以ViewHolder和item view是一一对应的关系。

ViewHolder主要解决了item view的复用问题,减少findViewById操作,增加列表性能。

RecyclerView和Listview的缓存机制对比

  • ListView的缓存:
    图一:


    image.png

    图二:


    image.png

    由图可以看出
    ListView 的缓存由RecycleBin来控制,分为两部分Active View 和Scrap View
  1. Active View是指在屏幕内的item view ,缓存这部分是因为android的屏幕帧是一直在更新的,当更新的屏幕的时候如果item view 已经在屏幕内的是直接会被拿来复用的,不会在走getView方法绑定数据
  2. Scrap View是指划出屏幕的item view, 被回收利用放入Scrap View中,当下以个item view要显示的时候会先到Scrap View中去查找有没有view type同类型的可以复用,如果有直接拿来绑定数据,如果没有找到会Create View然后在绑定数据。

总结:凡是Active View直接拿来复用的不会走getView绑定数据方法,凡是Scrap View不管是创建View还是复用View都会走getView方法来绑定数据。

  • RecyclerView的缓存:
    图一:


    image.png

    图二:


    image.png

RecyclerView和ListView的缓存对象有点不太一样,ListView缓存的额是item view 而 RecyclerView缓存的是ViewHolder,但是原理都是一样的,因为item view和ViewHolder本来就是一一对应的。RecyclerView的缓存由Recycler来控制,分为4个部分Scap, Cache,ViewCacheExtension,RecycledViewPool

1.RecyclerView的Scap相当于ListView的Active View,但是Scap查找缓存是通过position来实现的,可以直接拿来显示,都不需要在绑定数据。
2.RecyclerView的Cache相当于ListView的Scrap View,不同的地方是 Cache也是通过position来查找实现的,Cache缓存也是直接可以使用的View,如果用户反复小范围上下滑动,那么会通过查找Cache里面的positon直接显示,不需要走onBindViewHolder绑定数据,和Scap里面的效果一样,但是ListView是需要走getView重新绑定数据的。
3.ViewCacheExtension自定义缓存的item view,基本很少使用
4.RecycledViewPool 被废弃item的缓存池,从这里面被找出来复用的item会再次走onBindViewHolder方法重新绑定数据显示。

总结:RecyclerView缓存会先从Scap和Cache缓存通过positon来查找是否有直接可用的item显示,并且这两个缓存里面拿出来的item不会走onCreateViewHolder和onBindViewHolder方法,如果两个里面都没有那会走ViewCacheExtension自定义的缓存,最后会从RecycledViewPool里面去查找缓存,通过RecycledViewPool取到的缓存会执行onBindViewHolder方法重新绑定数据显示,如果所有的缓存都找不到,那么会走onCreateViewHolder方法创建View,并且会执行onBindViewHolder方法绑定数据。


小知识点 :如何统计列表中的广告显示次数

  • ListVIew中可以直接使用getView方法,因为不管是复用还是重新CreateView都需要走getView方法绑定数据。
  • RecyclerView使用onBindViewHolder吗?因为RecyclerView里面有一个Cache缓存的存在,Cache是通过position来实现的,当用户反复上下小范围滑动时,Cache缓存里面通过position拿出来的数据是可以直接展示的不会走onBindView,所以需要使用onViewAttachedToWindow方法来统计。

你可能不知道的RecyclerView 性能优化策略

  • 最好不要在onBindViewHolder里面设置监听
    在onBindViewHolder设置点击监听器会导致重复创建对象,因为监听只是一个观察者模式,并且ViewHolder和item view 是一一对应的,所以设置一次就可以进行监听,最好是放在onCreateViewHolder里面设置

  • LinearLayoutManager.setInitalPrefetchItemCount(横线列表初次显示时可见的item个数)
    对使用竖向的RecyclerView里面有嵌套一个横向的RecyclerView进行优化,如果滑到了横向滑动item RecyclerView时由于创建更多的子View,会引起页面卡顿。由于RenderThread的存在,RecyclerView会进行prefetch,所以可以使用LinearLayoutManager.setInitalPrefetchItemCount()。值得注意的是只有LinearLayoutManager有这个API,并且只能在嵌套内部的RecyclerView的LinearLayoutManager调用才生效

  • RecyclerView.setHasFixedSize()
    设置是否有固定的Size,如果Adapter的数据变化不会导致RecyclerView的大小变化,直接调用RecyclerView.setHasFixedSize(true)

  • 多个RecyclerView共用RecycledViewPool
    使用场景在ViewPager+Fragment+RecyclerView中有相同的列表item。

      var recycledViewPool = RecyclerView.RecycledViewPool()
      recyclerView1.recycledViewPool = recycledViewPool
      recyclerView2.recycledViewPool = recycledViewPool
      recyclerView3.recycledViewPool = recycledViewPool
    
  • DiffUtil
    局部更新方法notifyItemXXX()不适用于所有情况
    notifyDataSetChange()会导致整个布局重绘,重新绑定所有的ViewHolder,而且会失去可能的动画效果
    DiffUtil适用于整个页面需要刷新,但是有部分数据可能相同情况


    image.png

    image.png

    image.png

    image.png

    image.png

    在列表很大的时候异步计算Diff
    1.使用Thread/Handler 讲DiffResult 发送到主线程
    2.使用RxJava 讲calculateDiff 操作放到后台线程
    3.使用Google 提供的AsyncListDiffer(Executor) / ListAdapter

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