如何让你的 RecycleView 帧率提高到55帧?

Posted 清风Coolbreeze

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何让你的 RecycleView 帧率提高到55帧?相关的知识,希望对你有一定的参考价值。

误区

网搜列表优化, 大多会建议我们从以下几个方面优化, 来提高列表的流畅度.

  1. 降低布局层级
  2. 缓存布局对象, 或者控制内存分配等
  3. 控制列表刷新范围

可我们经常面对的场景是:

  1. 业务复杂, 布局层级无法再简化
  2. 该缓存的布局由viewHolder做了, 该控制的缓存也都控制了
  3. 快速全列表滑动时, 肯定是全列表刷新的, 范围就是列表控件展示的所有Item.

这使得列表帧率的优化, 几乎无法推进.

其实这些建议都没有大毛病, 但是, 都没有抓到问题的症结.

症结

症结确实是 "Item的布局太复杂".

而这个"太复杂"会引起android Framework的两个方法非常耗时:

  • inflate()
  • onMeasure()

通过方法耗时检测工具, 我们可以确认这一点. 方法耗时检测工具设计方案看这里

这两个方法, 分别在列表滚动过程中, 当有新的item需要展示时, 会被调用.

如果你是用ViewHolder, 那么inflate()可能不会频繁调用. 但是onMeasure一定会调用.

因为列表控件需要通过这个方法确认item的大小.

复杂的布局xml文件, 会大量消耗inflate()的耗时, 这个很容易理解.

但是onMeasure()为什么会耗时? 其实还有另外一个因素影响onMeasure()的方法耗时:=

    android:layout_width="match_parent"
    android:layout_height="wrap_content"

match_parent wrap_content代表这个布局的尺寸是不确定的, 是依赖父布局或者子View的.

这就必须遍历父布局的尺寸, 或者遍历子View来确认当前Item的大小.

这是一个很耗时的过程, 会引起卡顿.

这就是症结了. 可我们面对的场景是, 布局复杂度没有办法再减了, 再减就要砍需求了.

怎么可能砍需求...被需求砍还差不多. . .

方案

好在确认了两个关键问题点

  • inflate()
  • onMeasure()

我们可以针对这两个方法做优化.

  1. 阶段绘制
  2. 尺寸缓存

阶段绘制

针对inflate()耗时. 我们可以将Item的inflate()过程, 拆分成至少两个阶段.

  1. 滚动时仅仅初始化ItemView框架, 忽略细节布局
  2. 滚动停止后再初始化/绘制ItemView细节布局

当列表在快速滚动时, 用户其实看不清每个Item的细节. 所以只要初始化一个简单的框架即可.

框架布局简单, inflate会很快

当列表停止后, 此时, 每个正在展示的Item, 在去inflate细节布局, addView()到ItemView, 绘制细节内容即可.

阶段性绘制的方案很多, 可以监听滚动(不推荐). 或者在ViewHolder中使用handler#sendMessage()/ #removeMessage(). 不扩展了.

难点

这里比较难的点是, 列表高速滚动时. 很难确认是否有过渡绘制的情况.

这里的过渡绘制是指, 同一个viewHolder, 多次绘制了不同Item的内容数据, 但是只有最后一个Item的数据展示了.

这种情况, 之前的绘制就浪费了, 而且容易引起卡顿. 这个问题需要通过日志小心甄别.

尺寸缓存

对于列表控件来说, 其实每一个Item的大小, 大多数情况下是固定的.

此时就可以利用缓存, 来提高onMeasure()的执行效率.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if(hasCache){
        //有缓存, 直接执行缓存结果.
        setMeasuredDimension(mWidthCache, mHeightCache);
    }else{
        //没缓存, 执行一次onMeasure()
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidthCache = getMeasuredWidth();//获取缓存
        mHeightCache = getMeasuredHeight();//获取缓存
        hasCache = true
    }
}

难点

  1. 如果你的Item内容, 可能会变化, 比如有个TextView设置为GONE, 就会改变Item的大小, 此时要及时更新缓存.

  2. addView/removeView, 也要及时更新缓存.

总结

症结

Item布局复杂导致以下方法耗时卡帧

  1. inflate()耗时
  2. onMeasure()耗时

方案

  1. 阶段绘制
  2. 尺寸缓存

如此优化之后, 让你的RecycleView帧率提高到55帧+

如果没有, 建议:

  1. 继续分成N阶段绘制
  2. 检查过渡绘制

以上是关于如何让你的 RecycleView 帧率提高到55帧?的主要内容,如果未能解决你的问题,请参考以下文章

一行代码让你的python运行速度提高100倍

OpenCV C++ 多线程提高帧率

Flutter帧率监控 | 由浅入深,详解获取帧率的那些事

如何提高vnc帧率

让你的English提高一个层次

【转载】让你的MATLAB运行效率更快一些吧!