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

相关推荐
●VON3 小时前
HarmonyKit | 鸿蒙新特性:HDS 沉浸光感与空间化视觉设计
华为·性能优化·单元测试·harmonyos·鸿蒙
●VON5 小时前
HarmonyKit | 移动端开发者工具类应用的现状与趋势
git·华为·性能优化·harmonyos·鸿蒙
ujainu小1 天前
并发性能测试:心跳监测与主线程阻塞分析
华为·性能优化·harmonyos
JouYY1 天前
聊一下复杂 Agent 架构的性能优化设计
性能优化·llm·agent
ujainu小1 天前
原生性能优化:6变量实现高效桥接
开发语言·华为·性能优化·harmonyos
谙忆10241 天前
图片是 Web 性能监控的重灾区:LCP 和 CLS 到底怎么测、怎么定位到具体那张图
性能优化
谙忆10242 天前
图片预加载与 fetchpriority 实战:吃透浏览器资源优先级抢到 LCP 图
性能优化
TPBoreas2 天前
MySQL性能优化面试全攻略
mysql·面试·性能优化
一孤程2 天前
PerfDog性能测试实战——从入门到性能优化全覆盖
性能优化·性能测试·测试·perfdog
谙忆10242 天前
图片懒加载实战:IntersectionObserver、LQIP 占位与 CLS 布局防抖
性能优化