你将学到
Flutter 的目标是提供 60 帧每秒 (fps) 的性能,或者是在可以达到 120 Hz 的设备上提供 120 fps 的性能。
对于 60fps 来说,需要在约每 16ms 的时候渲染一帧。
当 UI 渲染不流畅的时候,卡顿就随之产生了。举例来说,如果一帧花了 10 倍的时间来渲染,这帧就会被丢弃,动画看起来就会卡。
有句话叫“快的应用固然很好,但流畅的应用则更好。”如果你的应用渲染并不流畅,该怎么处理呢?从哪里着手呢?本文展示了应该从哪里着手,步骤以及可以提供帮助的工具。
备忘
应用的性能不只是由一次测量(measure)决定的。性能有时取决于原生速度,同时也取决于 UI 的流畅性,不卡顿。其他性能指标还包括 I/O 或者网速。本文主要聚焦于第二种性能(UI流畅性),但其中的大多数工具也能被用来分析其他性能问题。
分析 Dart 代码中的性能问题,可以参考 调试 Flutter 应用 页下的 跟踪 Dart 代码性能。
1. 分析性能问题
分析应用的性能问题需要打开性能监控图层(performance overlay)来观察 UI 和 GPU 线程。在此之前,要确保是在分析模式(profile mode)下运行,而且当前设备不是虚拟机。使用用户可能采用的最慢设备来获取最佳结果。
1.1 连接到物理设备
几乎全部的 Flutter 应用性能调试都应该在真实的 Android 或者 iOS 设备上以分析模式进行。通常来说,调试模式或者是模拟器上运行的应用的性能指标和发布模式的表现并不相同。 应该考虑在用户使用的最慢的设备上检查性能。
为什么应该在真机上运行:
各种模拟器使用的硬件并不相同,因此性能也不同—模拟器上的一些操作会比真机快,而另一些操作则会比真机慢。
调试模式相比分析模式或者发布编译来说,增加了额外的检查(例如断言),这些检查可能相当耗费资源。
调试模式和发布模式代码执行的方式也是不同的。调试编译采用的是“just in time”(JIT)模式运行应用,而分析和发布模式则是预编译到本地指令(“ahead of time”,或者叫 AOT)之后再加载到设备中。JIT本身的编译就可能导致应用暂停,从而导致卡顿。
1.2 在分析模式运行
除了一些调试性能问题所必须的额外方法,Flutter 的分析模式和发布模式的编译和运行基本相同。例如,分析模式为分析工具提供了追踪信息。
使用分析模式运行应用的方法:
在 Android Studio 和 IntelliJ 使用 Run > Flutter Run main.dart in Profile Mode 选项
在 VS Code中,打开
launch.json
文件,设置flutterMode
属性为profile
(当分析完成后,改回release
或者debug
):
"configurations": [
{
"name": "Flutter",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
}
]
From the command line, use the
--profile
flag: 命令行使用--profile
参数运行
关于不同模式的更多信息,请参考 Flutter 的构建模式选择。
当两张图表都是红色时,就要开始对 UI 线程(Dart VM)进行诊断了。
-
使用 Flutter Inspector -
从命令行启动 -
写入代码
2.1.1 使用 Flutter inspector
2.1.2 VS Code
showPerformanceOverlay
属性为
true
来展示 PerformanceOverlay widget:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
showPerformanceOverlay: true,
title: 'My Awesome App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'My Awesome App'),
);
}
}
saveLayer
的调用和当前已缓存的图片的检查。
2.2 定位 UI 图表中的问题
DevTools’ docs
页面来获取安装和使用指导。
2.3 定位 GPU 图表中的问题
saveLayer
的调用,许多对象间的复杂操作,还可能是特定情形下的裁剪或者阴影。
timeDilat
ion
属性来极大地放慢动画。
timeDilation
属性。
RepaintBoundary
saveLayer
方法是 Flutter 框架中最重量的操作之一。
更新屏幕时这个方法很有用,但它可能使应用变慢,如果不是必须的话,应该避免使用这个方法。
即便没有显式地调用
saveLayer
,也可能在其他操作中间接调用了该方法。
可以使用
PerformanceOverlayLayer.checkerboardOffscreenLayers
开关来检查场景是否使用了
saveLayer
。
saveLayer
来渲染。
在这种情况下,相比通过 widget 树中高层次的父 widget 操作,单独对每个 widget 来应用透明度可能性能会更好。
其他可能大量消耗资源的操作也同理,比如裁剪或者阴影。
saveLayer
的调用以及无用的处理。
saveLayer
的调用时,先问问自己:
应用是否需要这个效果?
可以减少调用么?
可以对单独元素操作而不是一组元素么?
RepaintBoundary
来缓存图片是个好主意, 当需要的时候 。
PerformanceOverlayLayer.checkerboardRasterCacheImages
开关可以检查哪些图片被缓存了。
RepaintBoundary widget
中来缓存。
虽然引擎也可能忽略 repaint boundary,如果它认为图像还不够复杂的话。
rendering library
中在调试性能问题时最有用的参数(和一个方法)。
main()
方法中设置,然后通过热重启应用。
-
debugDumpRenderTree()
在 widget inspector 中检视渲染树,而不是使用这个参数转储渲染树到文件。 使用 widget inspector 并选择 Render Tree 标签。 -
debugPaintLayerBordersEnabled
-
debugRepaintRainbowEnabled
可以在 widget inspector 打开 More Actions 菜单,并选择 Show Repaint Rainbow 来开启这个参数。 如果任何静态 widget 在循环七彩跑马灯,(比如说一个静态的头部控件),这些区域就有着额外的重绘边界。 -
debugPrintMarkNeedsLayoutStack
如果发现比预期更多的 layout,打开这个参数,(比如说时间轴上、分析文件中、或者是在一个 layout 方法的print
状态内)。 开启后,控制台会大量输出堆栈跟踪信息来展示为什么每个渲染对象被 layout 标记为 dirty。 -
debugPrintMarkNeedsPaintStacks
和debugPrintMarkNeedsLayoutStack
相似,作用于过度绘制。
-
卡顿 -
下载大小 -
电池性能 -
启动时间