App 流畅性的关键指标有 UI帧率,GPU帧率,我们期望它能达到 60fps,也就是16ms每帧。
以 profile / release 模式运行
为了获取最接近生产环境的数据,我们应该选择一台尽可能低端的真机,并且以 profile 模式或者 release 模式下运行app。
assert()
等
JIT(Just in time)
模式编译 dart 代码的,
而 profile
和 release
是提前编译为机器码 AOT(Ahead Of Time)
,所以 debug 会慢很多。
所以说我们在查看性能时候不要用debug 模式,之前我就是用debug模式,无论怎么优化,性能都满足不了要求,还以为是flutter自身的问题,但是都说Flutter的渲染效率还是很高的,原来是debug模式的问题。
Debug模式
调试页面开发时使用Profile模式
调试性能 开发时使用Release模式
部署发包时使用Debug模式可以在真机和模拟器上同时运行,此模式会打开所有的断言,包括debugging信息、debugger aids(比如observatory)和服务扩展。优化了快速develop/run循环,但是没有优化执行速度、二进制大小和部署。
命令flutter run
就是以这种模式运行的,通过sky/tools/gn --android
或者sky/tools/gn --ios
来构建应用的。
Release模式只能在真机上运行,不能在模拟器上运行:会关闭所有断言和debugging信息,关闭所有debugger工具。优化了快速启动、快速执行和减小包体积。禁用所有的debugging aids和服务扩展。这个模式是为了部署给最终的用户使用。
命令flutter run --release
就是以这种模式运行的,通过sky/tools/gn --android --runtime-mode=release
或者sky/tools/gn --ios --runtime-mode=release
来构建应用。
Profile模式只能在真机上运行,不能在模拟器上运行,基本和Release模式一致,除了启用了服务扩展和tracing,以及一些为了最低限度支持tracing运行的东西(比如可以连接observatory到进程)。
命令flutter run --profile
就是以这种模式运行的,通过sky/tools/gn --android --runtime-mode=profile
或者sky/tools/gn --ios --runtime-mode=profile
来构建应用。
headless test模式只能在桌面上运行,基本和Debug模式一致,除了是headless的而且你能在桌面运行。
命令flutter test
就是以这种模式运行的,通过sky/tools/gn
来build。
为了调试性能问题,我们需要在发布模式的基础之上,为分析工具提供少量必要的应用追踪信息,这就是分析模式。
除了一些调试性能问题必须的追踪方法之外,Flutter 应用的分析模式和发布模式的编译和运行是类似的,只是启动参数变成了 profile 而已。
我们可以在 Android Studio 中通过菜单栏点击 Run
=>Profile
=>main.dart
选项启动应用,
也可以通过命令行参数 flutter run --profile
运行 Flutter 应用。
菜单栏点击 Run
=> Profile...
注意
该过程第一次编译非常慢请耐心等待,后来就会快很多。
打开 launch.json
文件并设置flutterMode
为 profile
"configurations": [
{
"name": "Flutter",
"request": "launch",
"type": "dart",
"flutterMode": "profile" # 测试完后记得把它改回去!
}
]
flutter run --profile
File=>Settings
中搜索flutter
找到
打开Open Flutter Inspector view on app launch
选中 View > Tool Windows > Flutter Performance
.
第一个按钮会在应用中显示,最后按钮一个会减速,方便我们查看帧率
选中 View > Command Palette…
会显示一个 command 面板.
在命令面板中输入 performance
并选择 Toggle Performance Overlay
如果命令显示为不可用,需要检查 app 是否正在运行.
在 MaterialApp
或者 WidgetsApp
的构造函数中设置 showPerformanceOverlay
属性为 true
:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
showPerformanceOverlay: true, // 开启
title: 'My Awesome App',
home: MyHomePage(title: 'My Awesome App'),
);
}
}
上图演示了性能图层的展现样式。其中,GPU 线程的性能情况在上面,UI 线程的情况显示在下面,蓝色垂直的线条表示已执行的正常帧,绿色的线条代表的是当前帧。
同时,为了保持 60Hz 的刷新频率,GPU 线程与 UI 线程中执行每一帧耗费的时间都应该小于 16ms(1/60 秒)。
在这其中有一帧处理时间过长,就会导致界面卡顿,图表中就会展示出一个红色竖条,如下图所示。
如果红色竖条出现在 GPU 线程图表,意味着渲染的图形太复杂,导致无法快速渲染;而如果是出现在了 UI 线程图表,则表示 Dart 代码消耗了大量资源,需要优化代码执行时间。
图中有三条线,最下面的一条线为16ms,如果应用大部分都在16ms下,就优化的差不多了。
图表分别体现了 UI帧率 和 GPU帧率。如果出现了红色,说明对应的线程有太多work要做。
那先来了解一下 Flutter 中的4个主要线程分别承担了什么职责。
在运行app的过程中,观察爆红的地方和触发场景,进行分析。
如果是UI报红:
那么可能是执行了某个较耗时的函数?或者函数调用过多?算法复杂度高?
如果只是 GPU 报红:
那么可能是要绘制的图形过于复杂?或者执行了过多GPU操作?
saveLayer
操作保存为一个图层,最后给这个图层设置透明度。而saveLayer
开销很大,这里官方给出了一个建议:首先确认这些效果是否真的有必要;如果有必要,我们可以把透明度设置到每个子控件上,而不是父控件。裁剪操作也是类似。
RepaintBoundry
控件中,引擎会自动判断图像是否复杂到需要用repaint boundary,不需要的话也会忽略。
在内存优化方面,我们的目标是希望减少应用内存占用,减少被系统杀死的概率,同时尽可能的避免内存泄露,减少内存碎片化。
内存优化策略