跳至主要内容

Flutter 性能分析

有人说,“一个快速的应用程序很棒,但一个流畅的应用程序更好”。如果你的应用程序渲染不流畅,该如何修复?从哪里开始?本指南向你展示了从哪里开始,需要采取的步骤以及可以提供帮助的工具。

诊断性能问题

#

要诊断出现性能问题的应用程序,你需要启用性能覆盖层来查看 UI 和光栅线程。在开始之前,请确保你正在配置文件模式下运行,并且没有使用模拟器。为了获得最佳效果,你可能需要选择用户可能使用的最慢的设备。

连接到物理设备

#

几乎所有 Flutter 应用程序的性能调试都应在物理 Android 或 iOS 设备上进行,并且你的 Flutter 应用程序在配置文件模式下运行。使用调试模式或在模拟器上运行应用程序通常不能反映发布模式构建的最终行为。你应该考虑检查用户可能合理使用的最慢设备上的性能。

在性能模式下运行

#

Flutter 的配置文件模式编译并启动你的应用程序,几乎与发布模式完全相同,但具有足够的功能来允许调试性能问题。例如,配置文件模式会向性能分析工具提供跟踪信息。

按如下方式在配置文件模式下启动应用程序

  • 在 VS Code 中,打开你的launch.json文件,并将flutterMode属性设置为profile(完成性能分析后,将其改回releasedebug

    json
    "configurations": [
      {
        "name": "Flutter",
        "request": "launch",
        "type": "dart",
        "flutterMode": "profile"
      }
    ]
  • 在 Android Studio 和 IntelliJ 中,使用**运行 > 在配置文件模式下运行 Flutter main.dart**菜单项。

  • 从命令行使用--profile标志

    flutter run --profile

有关不同模式的更多信息,请参阅Flutter 的构建模式

你将从打开 DevTools 并查看性能覆盖层开始,这将在下一节中讨论。

启动 DevTools

#

DevTools 提供了诸如性能分析、检查堆、显示代码覆盖率、启用性能覆盖层以及逐步调试器等功能。DevTools 的时间线视图允许你逐帧调查应用程序的 UI 性能。

一旦你的应用程序在配置文件模式下运行,启动 DevTools

性能覆盖层

#

性能覆盖层在两个图表中显示统计信息,这些图表显示了应用程序中时间花费的位置。如果 UI 卡顿(跳帧),则这些图表可以帮助你找出原因。图表显示在正在运行的应用程序的顶部,但它们不像普通小部件那样绘制——Flutter 引擎本身绘制覆盖层,并且对性能的影响最小。每个图表表示该线程的最后 300 帧。

本节介绍如何启用性能覆盖层并使用它来诊断应用程序中卡顿的原因。以下屏幕截图显示了在 Flutter Gallery 示例上运行的性能覆盖层

Screenshot of overlay showing zero jank
性能覆盖层显示光栅线程(顶部)和 UI 线程(底部)。
垂直绿色条表示当前帧。

解读图表

#

顶部图表(标记为“GPU”)显示光栅线程花费的时间,底部图表显示 UI 线程花费的时间。图表上的白线显示沿垂直轴的 16ms 增量;如果图表超过这些线中的任何一条,则表示你的运行速度低于 60Hz。水平轴表示帧。只有当你的应用程序绘制时,图表才会更新,因此如果它处于空闲状态,图表将停止移动。

覆盖层应始终在配置文件模式下查看,因为调试模式的性能有意牺牲以换取旨在帮助开发的昂贵断言,因此结果具有误导性。

每个帧都应在 1/60 秒(大约 16ms)内创建和显示。超过此限制的帧(在任一图表中)将无法显示,从而导致卡顿,并且在一个或两个图表中会出现垂直红色条。如果在 UI 图表中出现红色条,则表示 Dart 代码过于昂贵。如果在 GPU 图表中出现垂直红色条,则表示场景过于复杂,无法快速渲染。

Screenshot of performance overlay showing jank with red bars
垂直红色条表示当前帧渲染和绘制的成本都很高。
当两个图表都显示红色时,请先诊断 UI 线程。

Flutter 的线程

#

Flutter 使用多个线程来完成其工作,尽管覆盖层中只显示了两个线程。所有 Dart 代码都在 UI 线程上运行。虽然你无法直接访问任何其他线程,但你在 UI 线程上的操作会对其他线程的性能产生影响。

平台线程
平台的主线程。插件代码在此处运行。有关更多信息,请参阅 iOS 的UIKit文档或 Android 的MainThread文档。此线程未在性能覆盖层中显示。
UI 线程
UI 线程在 Dart VM 中执行 Dart 代码。此线程包含你编写的代码以及 Flutter 框架代表你的应用程序执行的代码。当你的应用程序创建并显示场景时,UI 线程会创建一个图层树(一个包含与设备无关的绘制命令的轻量级对象),并将图层树发送到光栅线程以在设备上渲染。不要阻塞此线程!显示在性能覆盖层的底行。
光栅线程
光栅线程通过与 GPU(图形处理单元)通信来获取图层树并显示它。你无法直接访问光栅线程或其数据,但是,如果此线程速度缓慢,则这是你已在 Dart 代码中执行的操作的结果。Skia 和 Impeller(图形库)在此线程上运行。显示在性能覆盖层的顶行。请注意,虽然光栅线程为 GPU 光栅化,但线程本身在 CPU 上运行。
I/O 线程
执行否则会阻塞 UI 或光栅线程的昂贵任务(主要是 I/O)。此线程未在性能覆盖层中显示。

有关更多信息和视频的链接,请参阅 Flutter wiki 中的框架架构以及社区文章图层蛋糕

显示性能覆盖层

#

你可以按如下方式切换性能覆盖层的显示

  • 使用 Flutter 检查器
  • 从命令行
  • 以编程方式

使用 Flutter 检查器

#

启用 PerformanceOverlay 小部件最简单的方法是使用 Flutter 检查器,该检查器在DevTools中的检查器视图中可用。只需单击**性能覆盖层**按钮即可在正在运行的应用程序上切换覆盖层。

从命令行

#

使用命令行中的P键切换性能覆盖层。

以编程方式

#

要以编程方式启用覆盖层,请参阅性能覆盖层(位于以编程方式调试 Flutter 应用程序页面中的一个部分)。

识别 UI 图表中的问题

#

如果性能覆盖层在 UI 图表中显示红色,请先分析 Dart VM,即使 GPU 图表也显示红色。

识别 GPU 图表中的问题

#

有时场景会导致图层树易于构建,但在光栅线程上渲染成本很高。发生这种情况时,UI 图表没有红色,但 GPU 图表显示红色。在这种情况下,你需要找出你的代码正在执行什么操作导致渲染代码变慢。某些类型的负载对 GPU 来说更难处理。它们可能涉及对saveLayer的不必要调用、多个对象的相交不透明度以及特定情况下的剪辑或阴影。

如果你怀疑缓慢的原因是在动画期间,请单击 Flutter 检查器中的**慢速动画**按钮,将动画速度降低 5 倍。如果你想要更多地控制速度,也可以以编程方式执行此操作。

缓慢发生在第一帧还是整个动画?如果是整个动画,是否剪辑导致了速度下降?也许有另一种绘制场景的方法不需要使用剪辑。例如,将不透明的角覆盖到正方形上,而不是剪辑到圆角矩形。如果这是一个正在淡入淡出、旋转或以其他方式操作的静态场景,则RepaintBoundary可能会有所帮助。

检查屏幕外图层

#

saveLayer方法是 Flutter 框架中最昂贵的方法之一。它在将后处理应用于场景时很有用,但它可能会减慢应用程序速度,如果不需要,则应避免使用它。即使你没有显式调用saveLayer,也可能会代表你隐式调用它,例如在指定Clip.antiAliasWithSaveLayer(通常作为clipBehavior)时。

例如,也许你有一组使用saveLayer渲染的不透明对象。在这种情况下,可能更有效的是将不透明度应用于每个单独的小部件,而不是应用于小部件树中较高层的父小部件。对于其他可能代价高昂的操作(如剪辑或阴影)也是如此。

当您遇到对saveLayer的调用时,请自问以下问题

  • 应用程序是否需要此效果?
  • 是否可以消除其中任何调用?
  • 我是否可以将相同的效果应用于单个元素而不是一组元素?

检查未缓存的图像

#

使用RepaintBoundary缓存图像很好,在有意义的情况下

从资源角度来看,最昂贵的操作之一是使用图像文件渲染纹理。首先,压缩后的图像从持久存储中获取。图像被解压缩到主机内存(GPU 内存),并传输到设备内存(RAM)。

换句话说,图像 I/O 可能很昂贵。缓存提供了复杂层次结构的快照,以便在后续帧中更容易渲染。由于光栅缓存条目构建成本高且占用大量 GPU 内存,因此仅在绝对必要时才缓存图像。

查看 Widget 重建分析器

#

Flutter 框架旨在让创建非 60fps 和流畅的应用程序变得困难。通常,如果出现卡顿,则是因为存在简单的错误导致每帧重建的 UI 比所需的多。Widget 重建分析器可帮助您调试和修复由此类错误导致的性能问题。

您可以在 Android Studio 和 IntelliJ 的 Flutter 插件中查看当前屏幕和帧的 widget 重建次数。有关如何执行此操作的详细信息,请参阅显示性能数据

基准测试

#

您可以通过编写基准测试来衡量和跟踪应用程序的性能。Flutter Driver 库提供了对基准测试的支持。使用此集成测试框架,您可以生成指标来跟踪以下内容

  • 卡顿
  • 下载大小
  • 电池效率
  • 启动时间

跟踪这些基准测试可以让您在引入对性能产生不利影响的回归时得到通知。

有关更多信息,请查看集成测试

其他资源

#

以下资源提供了有关使用 Flutter 的工具和在 Flutter 中进行调试的更多信息