Android性能分析之实操

背景

在我的项目界面里有多个选择器(如下图)用于筛选发送网络请求,但是在点击选择器进行选择的过程中十分卡顿。

我就想着刚好学习一下使用Perfetto并锻炼自己性能分析的能力。因此,下面都是在我的第一人称的视角进行的分析,也是我自己如何使用Perfetto的分享。

过程

点击Android StudioProfiler,然后点击第三个System TraceSystem TraceAndroid Studio提供的Perfetto可视化分析工具,其底层就是Perfetto。下面的Start profiler task from有两种,分别是NowProcess StartProcess Start表示从应用进程启动开始计算,这个更适合研究App启动时的性能分析;Now表示的是从应用当前界面为基准,由于我研究单个页面的组件为何卡顿,不需要从启动App开始。因此,选择Now即可。

点击Start anyway后,Trace 便开始录制,此时需要你复现界面卡顿流程。录制完毕,即出现如下图界面。

首先看左边,从上往下看分别是时间轴、Lifecycle(生命周期)、Janky frames(卡顿帧)、MainThread(也可以说是UI Thread)、RenderThread,主要就看这几个。

  • 时间轴: 主要用于缩小发生卡顿的范围
  • 生命周期: 主要用于判断卡顿处于什么周期,适合与时间轴搭配缩小出现卡顿的范围,但是我没用上。
  • Janky frames: 这个最重要,Android要求60HZ下,每帧时间不超过1000ms/60,也就是16.6ms。也就是说绘制一帧的时间不超过16.6ms。如果某一帧超过了16.6ms,也就被称为Jank,也就是说Janky frames记录的是掉帧的片段。
  • MainThread: 在这里面主要是你的UI主线程,这里大部分是Compose布局、重组的业务。
  • RenderThread: 这个主要负责GPU渲染。

OK,大概的介绍就到这里,我们直接开始实操分析。

首先缩小时间轴,我录制的时候看到出现卡顿的时间是4s以后,那么就将时间轴控制在3-5秒左右

发现4s后的第一个Janky frmaes就很可疑,点击这个Janky然后按M键来将这个Janky frames高亮并居中显示。

这时候可以看右侧Analysis(如下图),发现Layout name为TX-弹出式窗口,恰好符合所描述的卡顿现象,其中Actual duration为237.86ms,远超16.6ms。

Main thread states表示这个Janky frame 里UI线程的情况,图中Running的状态为占据69.49%、Duration时长为26.31ms,这远超过正常范围。说明卡顿原因大概率在CPU处理上,比如Compose布局、重组、业务逻辑。

RenderThread states的Running时长为2.7ms,Runnable时长为221μs,这说明GPU渲染是没有问题的。

查看事件名发现Events里大量出现DrawFrames、query、syncFrameState、prepareTree、beginFrame。这些都是准备事件,因此推断点击选择框后popup首帧的窗口的准备成本很高,回到代码,找到DropdownMenu发现两点:

  1. expandedfalsetrue会触发重组
  2. options.forEach会一次性组合所有的选项

因为我们options数量多,所以点击时会一口气创建大量DropdownMenuItemText

假设:

  • 点击选择框后,expanded = true
  • Compose创建DropdownMenupopup
  • options.forEach一次性组合全部选项
  • 系统对popup window进行测量、布局、绘制、图层同步。

验证:

  • 限制选项数量
scss 复制代码
options.take(20).forEach { ... }
  • 打印数量
bash 复制代码
Log.d("SelectorDropdown", "$label options=${options.size}")

查看打印日志发现同一个下拉框会重复打印多次,说明它在重复重组,而不是只创建一次。所以这个卡顿的本质不是数据量大,而是点击触发了整棵选择器树的级联重组,再叠加DropdownMenu弹出窗口本身的首帧创建和绘制成本,就出现掉帧、卡顿现象。

解决方案:

  • 减小DropdownMenu创建PopupWindow的首帧成本

    • DropdownMenu/ExposedDropdownMenuBox改成页面内展开的Card + LazyColumn
  • 减少无意义状态触发

state.xxx.map {...}remember,避免每次重新构造选项列表

卡顿延迟从237.86ms->10.81ms

相关推荐
TrisighT1 天前
Electron 跑在鸿蒙 PC 上,单窗口和多窗口内存差 800MB?我抓了 5 组数据
性能优化·electron·harmonyos
jump_jump5 天前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化
小小工匠6 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
大鱼>6 天前
地平线BPU部署实战:YOLOv8在J5/X3上的算法适配与性能优化
算法·yolo·性能优化
醉颜凉6 天前
Elasticsearch高性能优化:Bulk API大规模数据导入性能调优全攻略
elasticsearch·性能优化·jenkins
隔窗听雨眠6 天前
C语言函数递归从入门到精通(下):性能优化与工程实践
c语言·算法·性能优化
昇腾CANN6 天前
【cann-samples系列】GroupedMatmul MX量化矩阵乘的深度性能优化实践
线性代数·性能优化·矩阵·昇腾·cann
霸道流氓气质6 天前
Spring Boot 微服务性能优化完全指南
spring boot·微服务·性能优化
步步为营DotNet6 天前
Blazor 与 Microsoft.Extensions.AI 在客户端性能优化中的协同应用
人工智能·microsoft·性能优化
不能只会打代码6 天前
边缘视频分析平台的架构设计与性能优化——从750ms到190ms的调优之路
java·spring boot·redis·性能优化·边缘计算·物联网竞赛