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

相关推荐
NutShell Wang6 小时前
Firecrawl anydoc 实战拆解:用一个 Rust 依赖吃下 14 种文档格式
性能优化·rust·vibe coding
raindayinrain8 小时前
深入理解linux内核--文件页高速缓存,页框回收,性能优化
linux·性能优化·高速缓存·页框
Jay Kay9 小时前
BAGEL 训练性能优化报告
性能优化
_ZHOURUI_H_11 小时前
不做完整 ECS,只优化数据布局:Unity EasyECS 到底是什么
unity·性能优化·游戏引擎
虫小宝21 小时前
优惠券省钱APP查询性能优化:Elasticsearch与Canal实现的多维度商品搜索毫秒级响应方案
elasticsearch·性能优化·jenkins
李高钢1 天前
【WPF】高级 UI 与性能优化实战:从卡顿到丝滑
ui·性能优化·wpf
AI服务老曹3 天前
NVR视频流接入AI分析性能优化指南
人工智能·性能优化
show4333 天前
2026小程序端AI配音技术实现:TTS多音色引擎集成与MP3生成性能优化
人工智能·性能优化·小程序
NutShell Wang3 天前
Rust 1.97 实战迁移:v0 符号重整、Cargo 警告治理与位运算新 API
人工智能·后端·性能优化·rust·vibe coding
BK贝壳3 天前
Python底层:1亿个布尔值缓存失效标记,list爆内存numpy爆拷贝,bool-hybrid-array混合存储实测
数据结构·python·缓存·性能优化·list·numpy