分享好友 最新动态首页 最新动态分类 切换频道
recyclerview item 滑动 recycleview快速滑动优化
2024-12-26 13:26

recyclerview item 滑动 recycleview快速滑动优化

构建 Android App 界面时,RecyclerView 出场率很高。它的加载性能影响着用户体检。本篇分享一次完整的 RecyclerView 性能优化过程:从用工具定位问题,再不断尝试各种优化方案,最终达成 50% 的性能优化。

这次性能调优的界面如下:

界面用列表的形式,展示了一个主播排行榜。

这个排行榜嵌套在一个 ViewPager 中。最初发现性能问题是因为滑动到该界面时,ViewPager 指示器的平移动画卡了一下,掉帧了。

虽然卡顿是肉眼可见的,但若不能量化卡顿,就无法量化优化程度。

第一个想到的工具是GPU 呈现模式分析。开启它的路径如下:打开手机设置 — 开发者选项 — GPU 呈现模式分析 — 在屏幕上显示为条形图:

开启后,绘制性能就会图形化展示如下:

果然有很大的性能问题,柱子都快冲出屏幕了。

虽然图形化很直观,但量化地还不够细致,绘制耗时最好能精确到毫秒。所以转战到另一种方式“在 adb shell dumpsys gfxinfo 中”。选中后,打开排行榜界面,然后输入命令adb shell dumpsys gfxinfo <包名>,最近 n 针的渲染时长就会罗列如下:

每一行代表一帧渲染中各个阶段的耗时。

用另一个命令还可以得到更加精确的数据:adb shell dumpsys gfxinfo <包名> framestats,该命令会从应用生成的最近 120 个帧中输出带有纳秒时间戳的帧时间信息:

原生输出信息没有可读性,但它们遵守 csv 格式,复制粘贴到 wps 表格中,选中 数据 — 分列,用“逗号”分割:

数据就以表格的形式展示:

每一行表示一帧绘制的时间信息,一共有 16 列,每一列表示一个关键节点的时间戳,比如PerformTraversalsStart表示绘制遍历的开始时间点,DrawStart表示onDraw()开始的时间点,前者减去后者表示measure + layout的耗时。

利用表格的求差功能可以计算出一排表征性能的耗时。

虽然得到了量化数据,但是这么折腾着实有点辛苦。

一顿搜索之后,终于找到了下面这个高效的方法:

Window.addOnFrameMetricsAvailableListener()方法可以监听最近 120 帧的绘制耗时,它的数据源和上面 adb 命令是一样的。

我把自己感兴趣的耗时都打印了出来,分别是 measure + layout、延迟、动画、触摸、绘制、总耗时。

然后打开了排行榜界面,得到了如下数据:

有一帧绘制耗时高达 435 ms,其中 measure + layout 占了 370 ms。(此数值在不同手机上差异较大)

然后我关闭了 log 过滤,发现了更多信息:

紧接着耗时最长的那一帧,有一条警告,它是由Choreographer打印的,表示此刻发生掉帧,而且掉了整整 23 帧。。。(关于 Choreographer 详细的源码解析可以点击读源码长知识 | Android卡顿真的是因为”掉帧“?)

首先想到的一个方案是:“弃用 xml”

onCreateViewHolder()执行在主线程,如果它执行耗时,势必会影响到也运行在主线程的绘制性能。

demo 中排行榜一共有两类 item:表头和表体,其中构建表头布局的代码如下:

原本这些逻辑应该写在RecyclerView.Adapter中,把它单独抽象到一个 proxy 类中,是为了解耦,以便更容易地为列表添加不同类型的表项:

调用addProxy()就动态地添加一种新表项类型(关于代理模式的实战应用可以点击代理模式应用 | 每当为 RecyclerView 新增类型时就很抓狂)。

在onCreateViewHolder()中通过解析布局文件的方式来构建表项 item。

但解析布局文件需要进行 IO 操作将布局文件读到内存中,再解析 xml 根据标签 new 出对应的控件实例,最后 addView() 到容器中。这个过程是耗时的。

如果能使用 kotlin 代码直接完成布局的构建,则可以加速这个过程。但这样的构建代码可读性很差,后期想要更改控件的某个属性很难定位。

利用 kotlin 的DSL来改善构建代码的可读性,甚至超越 xml:

关于如何使用 DSL 简化布局构建可以点击Android性能优化 | 把构建布局用时缩短 20 倍(下)

将表头和表体 item 都用DSL重构了一番,运行 demo 看看数据:

measure + layout 时间从 370 ms 缩短到 330 ms,可喜可贺~~

想到的第二个优化方案是:“替换表项的根布局”

表头和表头 item 的布局都是用了ConstraintLayout,是不是因为它太复杂了,所以导致measure + layout耗时过长?

带着怀疑,我把所有的ConstraintLayout换成了FrameLayout,界面就变成了这样:

所有子控件都聚拢在一点,再瞄一眼性能日志:

令人惊喜的是measure + layout时间从 330 ms 缩短到了 272 ms。

看来表项根布局的复杂程度的确可以影响到列表的加载性能,而且列表会放大这个性能差距,因为 n 个表项就会进行 n 次measure + layout

那就用最最简单的FrameLayout来布局吧~。通过leftMargin和topMargin来定位表项中的每一个子控件。我对着 UI 设计图,读取了每个子控件相对于父控件的左边距和上边距,然后用FrameLayout重写了表头 item。

但当我把 demo 在不同手机上运行之后,发现这个方案有缺陷,虽然已经使用了 dp 而不是像素值,但依然无法很好地解决多屏幕适配的问题:

“粉丝数”根据左边距和上边距来确定相对于父控件的位置,不同的手机屏宽度不同,所以适配效果很差。

可能这就是相对布局存在的原因,但RelativeLayout也不是省油的灯。有没有别的更简单的方法?

我想到了百分比布局,还是基于左边距和上边距,但这次不使用 dp 值,而是用相对于父控件的百分比,不就能完美解决这个问题吗?

立马搜索了一下,遗憾的发现PercentFrameLayout已经被弃用。。。

那就自己手写一个:

百分比布局的编码很简单,只需要两步:先测量所有子控件,然后按需要定位所有子控件。其中测量孩子使用ViewGroup.measureChildren()就完成了。布局孩子得先计算出父控件的宽高,然后与子控件的百分比相乘就得到了相对于父控件的位置,最后调用View.layout()来定位子控件。

运行一下 demo,效果理想~~

运用相同的思路重构了一下表体 item 。过程中发现了一个问题:并不是所有控件都可以相对于父控件来布局。

比如下面这个场景:

表项数据是服务器返回的,文字长度是可变的,“等烟雨来,就是不来”后面与它垂直对齐的图片就无法相对于父控件布局。

所以PrecentLayout不得不也引入相对布局的概念,但也不需要像ConstraintLayout那样复杂,一个简化版的百分比相对布局如下:

  • PercentLayout使用了SparseArray来存储子控件 id 和子控件引用的对应关系。其实只要拿到了View就可以拿到它的 id,为啥还要特意将这些信息存储在一个 map 结构中?因为想用空间换一点时间,否则每次都得遍历所有子控件。使用SparseArray而不是HashMap也是出于节约内存的考虑,相对而言,它有更好的内存效率,详细分析可以点击内存优化:充满矛盾的SparseArray。
  • 为PercentLayout新增了一系列相对布局属性,这些属性的语义和ConstraintLayout中的一样。但有两个比较特殊的:centerHorizontalOf表示相对于某个控件水平对齐,centerVerticalOf表示相对于某个控件垂直对齐。
  • 这一系列相对布局属性存在互斥关系,他们分为两组,一组横向,一组纵向(详见代码注释)。一个控件只能拥有一个横向属性和一个纵向属性。getChildLeft()和getChildTop分别遍历所有的横向和纵向属性,根据不同的相对位置采取不同的计算方法,以确定子控件相对于父控件的 left 和 top。

然后就可以像这样构建表体 item 的布局:

把 demo 跑起来,measure + layout 的耗时如下:

measure + layout用了 288 ms,虽然相对于FrameLayout多了十几毫秒,但是和ConstraintLayout的 330 ms 相比还是有不小的提升。

measure + layout耗时从最开始的 370 ms 经过两次优化,分别是弃用 xml和替换表项根布局,缩减到 288 ms,有了 22% 的性能提升。但是离“耗时减半”还有点距离。限于篇幅原因,后续的优化详解放到下一篇继续讲解。欢迎关注我,以及时获取博客更新。

最新文章
热门中高端手机大排行 京东焕新季补贴10%起
如今,对于手机品牌而言,要想掌控市场话题权,必须要在中高端市场站稳脚跟。2022年已经过去四分之一,手机市场新机不断。眼下正是京东手机焕新季,用户换机需求也日渐高涨。面对琳琅满目的机型,用户多少会不知道如何下手,京东各类手机热卖榜单
网站关键词优化步骤,让网站在搜索引擎中脱颖而出
随着互联网的快速发展,数量呈爆炸式增长,如何在众多网站中脱颖而出,吸引目标用户,成为企业关注的焦点。而网站优化是提升网站搜索引擎排名、吸引潜在客户的重要手段。本文将从关键词选择、布局、优化策略等方面,详细介绍网站关键词优化
PingPong深耕跨境支付场景创新,为企业出海提供本地化生态服务网络
  伴随西方传统节日临近,海外消费需求持续升温。从近日落幕的全球黑色星期五和网购星期一(下称黑五网一)购物季看,各大跨境电商平台战绩不俗,中国商家更是作出贡献的中坚力量。  11月份,制造业采购经理指数(PMI)新出口订单恢复至48.1%,
让你快速精通SEO优化
高转化页面的打造!这里210.30.208.205提醒大家,做SEO无论你技术怎么样,都要做好高转化页面的打造,否则你的SEO最终都会功 亏一篑,无论排名如何,高转化页面的方式很多,销售信的引导,免费的引导,低价的引导,绝对化优势的引导,做好
高清美女写真生成大揭秘:用AI技术打造你的虚拟女友
在当今这个科技飞速发展的时代,AI技术不仅改变了我们的工作生活方式,也彻底颠覆了我们的艺术审美。想象一下,你在朋友圈中悄悄发布了一张梦幻般的美女写真,朋友们纷纷点赞,而这张照片的创作过程仅需几分钟。这并不是未来科幻片中的情节
花30万读个港硕,我后悔了
“最新消息,今年可能不考虑港硕了。”看到秋招群里的对话,七月心底五味杂陈,本科毕业时,她陪同学去比亚迪面试,在现场亲眼见证了比亚迪对海归派的求贤若渴、来者不拒。如今,她读了个港硕、捧着QS名校研究生毕业证回来,面对的却是通道
楼上半夜又唱又跳,楼下房客睡不着写投诉信!长沙一直播公司:会装隔音棉
楼上半夜直播楼下住户睡不着网友入住酒店发现楼板嗡嗡作响,直播公司承诺会装隔音棉潇湘晨报记者 於广强 长沙报道近日,有网友反映,在长沙县一家电竞酒店入住,楼上一家直播公司半夜直播声音很大,影响休息,希望改进。记者现场探访发现,
鸿蒙系统用什么开发的
鸿蒙系统是由华为自主研发的分布式操作系统,主要采用微内核设计,使用了C/C++和Java作为主要开发语言。 鸿蒙系统的开发主要包括五个方面:微内核、分布式架构、ARK编译器、开发环境和应用生态。其中,微内核是鸿蒙系统的核心,而分布式架
磁力金牛是快手的吗?快手磁力金牛怎么赚钱
摘要:磁力金牛是快手上一种赚钱工具,但不属于快手自己的产品。磁力金牛通过推广任务赚取佣金,并提供了多种赚钱方式,如推广商品、分享任务等。本文将从磁力金牛的概念、使用方法、赚钱方式和注意事项四个方面进行详细解析。1、磁力金牛
华为nova 13/Pro手机正式进军欧洲市场:搭载EMUI 14.2,起售价549欧元
IT之家12月13日消息,在科技界备受瞩目的华为nova 13系列,终于在今年10月22日以惊艳的姿态发布,并在最新宣布后成功进军欧洲市场。伴随着全球市场的日益竞争,这款手机的表现无疑是各方关注的热点。华为nova 13/Pro系列手机均搭载强劲的麒
相关文章
推荐文章
发表评论
0评