RecyclerView Prefetch功能探究

来源:互联网 发布:9wifi九维网络客服 编辑:程序博客网 时间:2024/06/13 17:58

在Android的开发中,滚动列表是一个出镜率非常高的组件。这其中RecyclerView是当然不让的明星。从Native到weex,RecyclerView有着非常广泛的使用。正因如此,我们持续不断地针对RecyclerView页面进行优化。在最近一次调研RecyclerView优化的过程中,偶然看到google在最新版版本的RecyclerView中增加了Prefetch功能。Prefetch的功能到底做了哪些事情,是不是真的能提升页面性能,使用的成本有多高?

针对这些问题,本文将分三步来做一个简单的探究:

  • (1)功能怎么用
  • (2)效果怎么样
  • (3)原理是什么

1. Prefetch功能的使用

在研究Prefetch功能的实际效果之前,首先需要能正常使用Prefetch功能。

  • 第一步 :升级 Support Library的版本到 25.3.1。

    google官方在 Support Library v25 版本中,为RecyclerView增加了Prefetch。
    并且在 v25.1.0 以及25.3.0版本中进行了完善。在最新的稳定版本25.3.1中已经基本稳定。
    如果需要使用建议升级到25.3.1版本。详细可见support-library change log

    注意: android support v4 包从24.2.0版本开始拆分成了多个更小的模块。

    包括:support-compat,support-core-utils,support-core-ui,support-media-compat以及
    support-fragment。如果出现使用

    compile group: 'com.android.support', name: 'support-v4',version: '25.3.1', ext: 'jar'

    出现了类找不到的情况,需要按照需要依赖上述子包。

  • 第二步:实现LayoutManager

    • 如果你使用的是官方的LayoutManager,那么直接可以获取Prefetch的功能,无需任何其他的定制。

    • 但是如果你使用的是自定义的LayoutManager,需要重写LayoutManager.collectAdjacentPrefetchPositions()
      方法。如果嵌套RecyclerView使用需要复写setInitialPrefetchItemCount
      详细信息可以参见具体的API文档(点击直接跳转)

    • 第三步:设置打开Prefetch

    默认情况下Prefetch的功能是打开的。当然也可以手动选择关闭:

    LayoutManager.setItemPrefetchEnabled(false);

    所以确认一下不要手动关闭预取功能

    在完成上述三个步骤以后,可以像往常一样使用RecyclerView,并且自带了prefetch功能。

2.Prefetch实际效果

RecyclerView的Prefetch功能正常运转以后。那么这个功能到底能带来多少性能的收益是现在需要确认的。分几个部分来看:

demo
  • demo内容:
    使用Android官方提供的StaggeredGridLayoutManager实现了一个瀑布流。
    瀑布流中文字的数量和图片的大小都是可以变的。展示如下:

衡量标准&方法:
  • GPU呈现模式分析 打开的GPU渲染条形图。图中的绿线是流畅度的分割线。超过绿线的帧都是渲染超时的。比较的核心指标是滑动过程中,超出绿线frame占比。

    (注:流畅的标准是1s 60帧 一帧的渲染时间大约是16ms)
  • 测试设备小米5s

  • 数据绑定复杂度模拟:
    为了检测预取功能在不同的页面渲染复杂度情况下的实际效果。在RecyclerView数据绑定函数:onBindViewHolder函数中,使用

  Thread.sleep(time);

来模拟页面渲染的复杂度。复杂度的大小,通过time时间的长短来体现。时间越长,复杂度越高。

原始demo流畅度对比
打开prefetch 关闭prefetch

在原始demo中,由于复杂度有限,无论是打开或者关闭prefetch功能,滑动过程中无明显差异。

延时8ms 流畅度对比
打开prefetch 关闭prefetch

通过延时8ms,整个数据绑定的复杂度急剧上升。在关闭prefetch功能的情况下,在16ms 内无法完成的渲染的帧数明显增多。相比之下打开prefetch以后,页面依然非常流畅。

延时12ms 流畅度对比
打开prefetch 关闭prefetch

通过延时12ms在关闭prefetch功能的情况下,在16ms 内无法完成的渲染的帧数进一步增多,页面卡顿愈加明显。但是打开prefetch以后,页面依然非常流畅。

延时17ms 流畅度对比
打开prefetch 关闭prefetch

通过延时17ms,在16ms时间内完成渲染已经无法做到。滑动过程中,滑动卡顿可以明显被感知。在关闭prefetch功能的情况下,在16ms内无法完成的渲染的帧数进一步增多,并且延时也更长。打开prefetch以后,同样不可避免的会出现16ms无法渲染帧,但是页面对比下还是更加流畅。

结论:基于上述实验,可以得出以下几个结论:
  1. 使用prefetch以后,recyclerView的流畅度对页面复杂度的敏感性降低。

  2. 在高复杂度(recyclerView的子项view的创建和绑定更加耗时)情况下,prefetch功能确实能显著提高页面流畅度。

3.Prefetch原理:

android 5.0以后,android系统为了提高UI渲染的效率引入了RenderThread。通过这样的设计,将主线程从UI
渲染的繁重工作中解脱出来。在UI渲染过程中,主线程可以更加专注于跟用户进行交互。这样可以大大提高页面流畅度。
通过Systrace工具跟踪一个页面,获取页面渲染详细数据。

  注意:如果使用DDMS来抓取。一定要记得在Enable Application Traces from 中选择APP所在的进程。否则你可能无法观察到Prefetch的渲染。如下图所示:

一个常规页面的渲染流程如下:

通过上面的展示,UI线程准备页面数据并且在ready以后交由Render线程渲染。帧与帧之间通过帧同步信号sync来同步。如果页面渲染无法在规定时间内完成,就会出现丢帧情况渲染的时序会变成如下情况:

不难发现在正常渲染过程中,有一个非常的明显的特点:在UI线程将页面数据交由Render
线程渲染以后,会出现大量的空闲时间。如下图所示:

这些空闲等待时间就被浪费了。Prefetch的核心思想就是利用这部分空闲时间来预先处理item的创建和数据绑定[2]。

对比一下使用了Prefetch以后的渲染时序图如下:

时空上的复用,会大大提高页面渲染的效率,提高页面流畅度。

4. 收益

在实际开发的过程中,任何的升级都需要考量投入产出的比例。Prefetch优化影响的页面包括:

  • (1)Native页面中使用RecyclerView的页面
  • (2)如果你使用了weex,list组件的性能同样会得到提升

    注意: 只有Android 5.0以后版本的手机才能获得这个优化。

    通过简单的升级(可能需要解决一些兼容问题),就可以使几乎所有的RecyclerView页面性能从中受益。并且在sourceCode层面不需要做额外的定制(自定义和嵌套除外)。投入产出比还是非常可观的,所以

    建议大家尽快升级吧!

5. 引用

  • [1]Android应用程序UI硬件加速渲染的动画执行过程分析

  • [2]RecyclerView Prefetch 注意翻墙

  • [3]Profile GPU Rendering Walkthrou

  • [4]Android Support Library

  • [5]Systrace

3 0
原创粉丝点击