RecycleView可优化的点
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RecycleView可优化的点相关的知识,希望对你有一定的参考价值。
参考技术A因为onBind方法的调用时机是View滑到屏幕内可显示时这个方法就会调用此方法,避免在这个方法内设置点击事件等创建对象的操作。
多用于RecycleView嵌套RecycleView,减少对ViewHolder的创建
notifydatasetchange全部刷新,notifyItemRemoved(0)局部删除, notifyItemRangeInserted(start,end)等刷新机制的选择。
其实setItemViewCacheSize设置的是CacheViews的大小
所以我们可以适当的通过调用setItemViewCacheSize方法,来增加CacheViews的大小(默认是2),来防止小范围的滑动导致的重复Bind而导致的卡顿。 典型的拿空间还时间,所以要考虑内存问题,根据自己的应用实际情况设置大小
当知道Adapter内Item的改变不会影响RecyclerView宽高的时候,可以设置为true让RecyclerView避免重新计算大小。
需要升级Recycle版本到25以上的版本才能使用recyclerview prefetch功能。
参考文章: RecyclerView的新机制:预取(Prefetch)
RecyclerView Prefetch功能探究
如何让你的 RecycleView 帧率提高到55帧?
误区
网搜列表优化, 大多会建议我们从以下几个方面优化, 来提高列表的流畅度.
- 降低布局层级
- 缓存布局对象, 或者控制内存分配等
- 控制列表刷新范围
可我们经常面对的场景是:
- 业务复杂, 布局层级无法再简化
- 该缓存的布局由viewHolder做了, 该控制的缓存也都控制了
- 快速全列表滑动时, 肯定是全列表刷新的, 范围就是列表控件展示的所有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()
我们可以针对这两个方法做优化.
- 阶段绘制
- 尺寸缓存
阶段绘制
针对inflate()耗时. 我们可以将Item的inflate()过程, 拆分成至少两个阶段.
- 滚动时仅仅初始化ItemView框架, 忽略细节布局
- 滚动停止后再初始化/绘制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
}
}
难点
-
如果你的Item内容, 可能会变化, 比如有个TextView设置为GONE, 就会改变Item的大小, 此时要及时更新缓存.
-
addView/removeView, 也要及时更新缓存.
总结
症结
Item布局复杂导致以下方法耗时卡帧
- inflate()耗时
- onMeasure()耗时
方案
- 阶段绘制
- 尺寸缓存
如此优化之后, 让你的RecycleView帧率提高到55帧+
如果没有, 建议:
- 继续分成N阶段绘制
- 检查过渡绘制
以上是关于RecycleView可优化的点的主要内容,如果未能解决你的问题,请参考以下文章
ListView嵌套RecycleView滑动卡顿问题的优化方案