Skip to main content

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 示例上运行的性能覆盖层:

显示零卡顿的覆盖层的屏幕截图
性能覆盖层显示光栅线程(顶部)和 UI 线程(底部)。
垂直的绿色条代表当前帧。

解读图表

#

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

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

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

显示带有红色条的卡顿的性能覆盖层的屏幕截图
垂直的红色条表示当前帧的渲染和绘制成本很高。
当两个图表都显示红色时,首先诊断 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 图表中显示红色,则即使 GPU 图表也显示红色,也应首先分析 Dart VM。

识别 GPU 图表中的问题

#

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

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

卡顿发生在第一帧还是整个动画过程中?如果是整个动画,是裁剪导致速度变慢吗?也许有其他绘制场景的方法不需要裁剪。例如,在正方形上叠加不透明的角,而不是裁剪成圆角矩形。如果这是一个正在淡入淡出、旋转或以其他方式操作的静态场景,则RepaintBoundary 可能会有所帮助。

检查屏幕外图层

#

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

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

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

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

检查未缓存的图像

#

使用RepaintBoundary缓存图像很好, 当它有意义的时候

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

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

查看小部件重建分析器

#

Flutter 框架的设计使得很难创建非 60fps 和不流畅的应用程序。通常,如果你有卡顿,那是因为有一个简单错误导致每一帧重建的 UI 比所需的多。小部件重建分析器可以帮助你调试和修复由这类错误引起的性能问题。

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

基准测试

#

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

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

跟踪这些基准测试使你能够在引入会对性能产生不利影响的回归时获得通知。

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

其他资源

#

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