前言
最近在学习Flutter应用性能优化,DevTools是官方推出的一个性能优化和调试工具,非常值得学习使用。由于没有看到比较系统的介绍文章,于是结合论坛的文章和官方文档总结一篇DevTools工具的入门教程。
一、什么是DevTools
DevTools是一套应用于Dart 和 Flutter 的性能调试工具,拥有非常全面的功能。
下面列出了一些可以用DevTools来实现的操作:
- 检查 Flutter 应用程序的 UI 组件布局和状态;
- 在 Flutter 应用程序中诊断 UI 性能较差的问题;
- 对 Flutter 应用进行 CPU 性能检测;
- 对 Flutter 应用进行网络性能检测;
- 对 Flutter 应用进行源码级的调试;
- 在 Flutter 应用中测试内存问题;
- 查看正在运行的 Flutter 应用的常规日志和诊断信息。
二、如何使用DevTools
DevTools中包括的分析工具如下图所示。在不同的编译模式下,工具的可用状态存在差异。
Debug 模式下
Profile 模式下
下面首先介绍一下三种编译模式以及每种模式的适用情况。
2.1 三种编译模式
Debug 模式:
在 Debug 模式下,app 可以被安装在物理设备或者模拟器上进行调试,开发者可以对应用进行断点调试、热重载。
Release 模式:
在 Release 模式下,不支持模拟器,断点调试不可用。
Profile 模式:
在 Profile 模式下,仅支持真机调试,有着和 Release 模式相近的性能,同时又有一些调试能力,足够用来分析APP性能。
🔔Tips
热重载功能仅能在调试模式下运行;
模拟器仅能在调试模式下运行;
在调试模型下,应用的性能可能会掉帧或者卡顿, profile 模式下会更接近真机性能。
使用总结
开发过程中,需要使用 热重载 功能,请选择 Debug 构建模式;
当你需要分析性能的时候,选择使用 Profile 构建模式;
发布应用的时候,需要选择使用 Release 构建模式。
2.2 启动DevTools
下面介绍三种安装和启动DevTools的方法
2.2.1 在Android Studio中使用
(1)安装Flutter和Dart插件
(2)打开一个Flutter应用,开始运行调试。可以连接真机或者使用模拟器,具体取决于采用的调试模式。
(3)在左下角点击下图中红框内的蓝色图标,将会启动DevTools
2.2.2 在VS Code中使用
(1)安装Flutter和Dart插件
(2)打开一个Flutter应用,点击Run > Debugging(F5),开启调试会话
(3)如果成功开启了调试,那么在 VS Code 命令控制板中搜索devtools将会显示 Dart: Open DevTools:选项。命令控制板可以通过Cmd + Shift + P组合键打开。
2.2.3 在命令行中使用
(1)将dart和flutter添加到环境变量
(2)在命令行中输入dart devtools,在命令行中将会看到类似下方的输出
在浏览器中也会打开一个如下的新窗口
(3)启动一个应用并进行调试,在日志中可以找到如下内容,将这个链接填入上一步的框中即可
Debug service listening on ws://127.0.0.1:64742/WQF2wM2iGIE=/ws
2.3 使用DevTools
在DevTools中有多个工具,下面逐个进行介绍
2.3.1 Flutter Inspector
Flutter inspector 可以用于可视化及查看 widget 树,帮助了解现有布局并诊断布局问题。
该工具仅在 Debug 模式下可用。
(1)在inspector中,主要包含如下图的五个区域,其中前三个区域是按键区,共包含八个按键;后两个区域是视图区,包括widget树和选中的某个widget的详细信息
(2)第一行中区域1和区域2的六个按键的功能
Select Widget Mode:点击该按钮后,将使设备上的应用程序进入「widget select」模式。当你点击应用界面上的任何 widget,将选中该 widget 并将 widget 树(区域4)滚动到对应的节点。再次点击该按钮则退出「widget select」模式。
这个模式可以帮助我们快速了解一个陌生项目的代码结构。
Show Animations:以五分之一的速度运行动画以便对它们进行优化
Show Guidelines:覆盖一层引导线以帮助调整布局问题
该功能会在你的应用顶层绘制引导线,展示绘制区域、对齐、间距、滚动视图、裁剪和空位填充。举例如下所示:
RenderBox 会加上一个浅蓝色的边框;对齐方式将以黄色箭头展示,这些箭头会显示出垂直和竖屏方向上 widget 相对其父布局的偏移
间距会以半透明的蓝色背景显示:
滚动视图,包含滚动内容的 widget(例如 ListView)会展示绿色的箭头:
使用了诸如 ClipRect 组件进行裁剪的内容,会以粉红色的虚线加一个剪刀图标展示:
空位填充的 widgets 会以灰色背景展示,例如没有 child 的 SizedBox:
Show Baselines:针对文字对齐展示文字的基线,基线是用来定位文字的水平的线。
在检查文字是否对齐时,基线会非常有用。例如,下图中文字的基线稍微有一些错位:
Highlight Repaints:高亮显示重绘内容,重新绘制时在图层上依次显示不同的颜色
该选项会为所有的 RenderBox 绘制一层边框,并在它们重新绘制时改变颜色。颜色改变按照彩虹色谱进行循环,有利于我们找到应用中频繁重绘导致性能消耗过大的部分。
例如,一个小动画可能会导致整个页面一直在重绘,可以修改代码将重绘范围缩小到其自身占有的区域:
Highlight Oversized Images:在运行的应用程序中高亮并反转显示消耗过多内存的图像,例如一张 5MB 大小的图片实际以 100x100 像素展示。注意,超过 128KB 的图片会被视为过大。
(3)区域3、4和5的功能
区域4展示了整个widget树结构;当在区域4中选中某个widget后,区域5会显示该 widget 的 Layout Explore 或详细信息,可以通过区域3中的两个按钮切换展示的信息。
1、Layout Explorer 不仅能让你查看整个布局界面,还可以让你做一些简单的动态操作,让你在不改动代码的前提下,明确你的布局问题,或者缺陷。
2、选择 Details Tree 标签展示选中 widget 的树结构的详细信息。
从树的详细信息中,你可以获取有关 widget 的属性、渲染对象和子节点等有用信息。
2.3.2 Performance
注:性能分析工具仅在 Profile 编译模式下使用,如果在 Debug 模式下使用会有如下的报错信息。
性能视图提供了应用活动的时间线以及性能信息。它由以下三个部分组成,且每个部分的粒度都更加细。
- Flutter 火焰图(仅支持 Flutter 应用)
- 时间线事件图
- CPU 监控
2.3.2.1 Flutter火焰图
Flutter火焰图如下,在图中,区域1内是开始、停止和清除按钮,区域2内是四个功能按键,区域3表示一帧图像的渲染。
(1)在区域2的四个按钮中,Performance Overlay会使性能图层显示到真机设备上。Raster 线程的性能显示在上面,UI 线程显示在下面。垂直的绿色条条代表的是当前帧。
(2)Enhance Tracing中包含如下几项增强功能。
如果想要在时间线中查看 build() 方法的事件,启用 Track Widget Builds 选项,时间线中将出现 widget 对应名称的事件;
如果想要在时间线中查看 RenderObject 布局构建的事件,启用 Track Layouts 选项;
如果想要在时间线中查看 RenderObject 的绘制事件,启用 Track Paints 选项。
(3)更多 debug 选项
通过开启和关闭时的性能对比,可以观察渲染裁剪、透明度和物理形状图层的影响。
在渲染层关闭的情况下,于构建帧图表里选择一个新的构建帧,查看它的时间线细节。如果 Raster 线程的时间消耗有显著降低,那么你禁用的效果的滥用可能是导致卡顿的主要原因。
渲染裁剪的图层
禁用该选项来检查已使用的裁剪图层是否影响了性能。如果禁用后性能有显著提升,请尝试减少你的应用中裁剪效果的使用。
渲染透明度图层
禁用该选项来检查已使用的透明度图层是否影响了性能。如果禁用后性能有显著提升,请尝试减少你的应用中透明度效果的使用。
渲染物理形状图层
禁用该选项来检查已使用的物理形状图层是否影响了性能,例如阴影和背景特效。如果禁用后性能有显著提升,请尝试减少你的应用中物理效果的使用。
(4)profile粒度级别调整
VM 收集 CPU 样本的默认速率为 1/250μs (即每 250 微秒收集一次数据)。一般情况下,Profile granularity 的默认值为 “medium”。可以通过页面顶部下拉列表进行修改。抽样率低、中、高粒度分别顺序对应 1/50μs、1/250μs 和 1/1000μs。
(5)条形图颜色
在区域3的条形图中,淡蓝色的是UI线程的绘制情况、深蓝色是栅格化线程(GPU线程),橙色代表绘制出现了卡顿,深红色表示帧参与了着色器渲染。
在Flutter里,为了使帧渲染频率达到 60 FPS ,每一帧的渲染时间必须等于或少于 16 ms。如果没有达到这个目标,则认为出现了卡顿。
如果是在 UI 图表出现了红色竖条,则表明 Dart 代码消耗了大量资源。而如果红色竖条是在 GPU 图表出现的,意味着场景太复杂导致无法快速渲染。
2.3.2.2 时间线事件图
当选择Flutter火焰图中的任一条形柱,则下方会显示该帧对应的时间线事件(Timeline Event)图。
在Timeline Event中,依然分为了两个部分,上半部分是UI Event,下半部分是Raster Event。图中展示的是自上而下的调用堆栈信息,即上面的堆栈帧调用下面的堆栈帧。每一个堆栈帧的宽度代表 CPU 执行的时长。栈帧消耗 CPU 的时间越长,就越有可能是我们进行性能改进的好地方。
2.3.2.3 CPU分析器
当选择时间线事件图中的任一子事件后,则下方会显示该子事件对应的CPU Profile。主要会用到的是Call Tree和CPU Flame Chart。
调用树视图是一种自上而下展示 CPU 中的调用堆栈信息方法。在Call Tree调用树中,可以看到子事件具体调用了哪些函数,以及各函数的耗时。
CPU火焰图选项卡主要用于显示一段持续时间内 CPU 的样本信息。在CPU Flame Chart中,以条形图的方式直观地观察各函数调用耗时。
注:Performance选项卡最下方的CPU Profile和DevTools中的CPU Profiler基本一致。
2.3.3 Memory
内存分析可以帮助我们查看在某一时刻内存使用情况,能够具体到某个类使用了多少内存,主动触发垃圾回收等。
2.3.3.1 内存分析图
内存分析图中颜色和图标的含义见下图中的标注。
当鼠标点击某一刻的折线时,会显示一个浮动窗口,显示内存使用具体数值。
默认进来的时候只有图中蓝色的区域,点击Android Menory会显示黄色区域,
点击Legend会显示图例说明,如下所示
2.3.3.2 SnapShot
单击1区域中的Take Heap Snapshot按钮,会显示当前内存对象表。当选中某个对象后,右侧区域会显示该对象实例占用的内存和存储的数据。
在区域2中可以按类名称,大小,分配的实例等对内存对象进行排序。
在右上角的搜索框还可以进行搜索。
除了看数据列表,还能以堆栈视图的方式查看内存分布,当选中TreeMap开关的时候,下面会呈现内存视图。当鼠标点击某一个块后,这个块会展开为更细粒度的使用情况,如下图
2.3.4 Debugger
使用Debugger功能需要采用Debug模式来编译。
在这里可以进行断点调试,对于命令行开发来说是一个便捷的调试工具。但是除了使用命令行工具开发以外,还是建议使用IDE进行调试,功能更强大一些。
2.3.5 Network
网络视图可以从Dart或Flutter应用程序检查HTTP网络流量,可以检查有关请求的常规信息,以及响应和请求标头。
如果需要更详细和准确的请求信息,还是建议使用Charles等专业的抓包软件来进行分析。
2.3.6 Logging
日志视图会展示 Dart 运行时和应用框架(比如 Flutter)的事件,以及应用级日志。
默认情况下,日志视图会展示:
- Dart 运行时的垃圾回收事件
- Flutter 框架事件,比如创建帧的事件
- 应用的 stdout 和 stderr 输出
- 应用的自定义日志事件
如果使用IDE开发的话,在运行时也会有日志输出,因此这个工具的使用率不高。
2.3.7 Provider
Provider视图会显示项目中用到的所有Provider,以及每个Provider中的变量信息
2.3.8 App Size
应用程序体积工具可用来分析应用的总体积。
我们可以使用 Analysis 标签 来查看「体积信息」的单个快照,或使用 Diff 标签 比较两个不同「体积信息」的快照。
「体积信息」:包含 Dart 代码、原生代码和非代码部分(比如应用包,资源和字体)。一个「体积信息」文件包含应用的所有图片数据。
2.3.8.1 生成体积文件
我们可以使用 --analyze-size 标志生成它:
flutter build <your target platform> --analyze-size
这会构建我们的应用并输出尺寸的摘要到命令行,同时告诉我们在哪里找到体积分析文件。
flutter build apk --analyze-size --target-platform=android-arm64
...
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
app-release.apk (total compressed) 6 MB
...
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
A summary of your APK analysis can be found at: build/apk-code-size-analysis_01.json
2.3.8.2 分析标签页
(1)读取一个体积文件
当打开分析标签页时,可以看到加载一个体积文件的使用说明。拖动一个尺寸文件到弹框中,并点击 “Analyze Size”。
(2)树状图的可视化表示
“分析” 标签页允许我们检查体积信息的单个快照。我们可以看到层次结构的树状图和表格,并且可以使用 “dominator tree” 和 “call graph” 看到代码的属性数据(例如:为什么编译后的应用程序中包含一段代码)。
在下面的视图中,空间被分解成矩形,其中每个矩形的面积与节点在编译后的应用程序中所占的大小成比例关系。在每个矩形(称为 A)的内部,还有更多的矩形存在于数据层次结构的更深层(A 的子级)。
(3)dominator tree
dominator tree 是一个树形结构的图表,其子节点可以立刻被支配。
如果通往 b 的每条路径都必经节点 a,那么我们可以说:节点 a 支配了节点 b。
把它放在应用程序大小分析的上下文中,想象一下 package:a 导入了 package:b 和 package:c,并且 package:b 和 package:c 都导入了 package:d。
package:a
|__ package:b
| |__ package:d
|__ package:c
|__ package:d
在这个例子中,package:a 支配 package:d,所以这个支配树看起来像是这样:
package:a
|__ package:b
|__ package:c
|__ package:d
这些信息可以帮助我们理解编译后的应用程序中为何出现某些代码片段。例如,如果我们正在分析应用程序的体积,并发现编译后的应用程序中包含意外的包,则可以使用支配树来跟踪包到其根源。
(4)call graph
call graph 提供了与 dominator tree 相似的信息。
它并不像 dominator tree 一样提供了一对多的代码体积数据节点,而是展示了代码体积数据节点之间存在的多对多关系。
我们再来看下面这个例子:
package:a
|__ package:b
| |__ package:d
|__ package:c
|__ package:d
此数据的 call graph 会将直接调用者 package:b 和 package:c 与 package:d 链接到一起,而不是它的「支配者」 package:a。
package:a --> package:b -->
package:d
package:a --> package:c -->
这些信息对于理解代码片段(包、库、类和函数)之间的细粒度依赖关系非常有用。
总结:如果想理解应用程序中包含一段代码的 根本 原因,请使用 dominator tree。如果想理解一段代码之间的所有调用路径,请使用 call graph。
2.3.8.3 差异标签页
diff 标签页可以比较体积信息的两个快照。
要比较的两个体积信息文件应该从同一个应用程序的两个不同版本生成,例如,在更改代码之前和之后生成的两个体积文件。
(1)读取体积文件
当打开 Diff 标签页时,可以看到加载「旧」和「新」大小文件的使用说明。将对应的文件拖放到各自的对话框中,然后单击 Analyze Diff。
(2)树状图的可视化对比
我们可以使用树状图和表格可视化两个文件之间的差异。在这个视图中,只会显示导入的两个文件中的差异数据。
三、总结
DevTools可以帮助开发人员快速找到和解决Flutter应用程序中的问题。它提供了一种交互式的方式来检查应用程序的状态,并允许查看应用程序的代码、变量、错误信息等。
总之,DevTools是Flutter开发中非常重要的工具,可以帮助开发人员快速找到和解决问题,并提高应用程序的性能和稳定性。
参考链接
flutter.cn/docs/develo…juejin.cn/post/689710…www.jianshu.com/p/7d8e5e067…