首先,在做性能调优之前,我们应该对flutter相关基础知识有一定的了解,不然我们无从做起,首先,我们要了解flutter是干嘛的--Flutter 是谷歌2018年发布的跨平台移动UI框架。
然后,他相对于其他跨平台开发框架来说,是高效的,至于他为什么高效,原因是因为:
他是直接调用Skia框架,而其他框架需要借助现有的原始框架来转一下,在才开始去调用Skia框架,这一来二去,肯定就有所消耗,这是其一,然后,我们还需要知道flutter的四个线程。
分别是:
该平台的主线程。插件代码在这里运行。更多信息请参阅:iOS 的程序 (UIKit) 文档,或者 Android 的主线程 (MainThread) 文档。性能图层并不会展示该线程。
UI 线程在 Dart VM 执行 Dart 代码。该线程包括开发者写下的代码和 Flutter 框架根据应用行为生成的代码。当应用创建和展示场景的时候,UI 线程首先建立一个 图层树(layer tree) ,一个包含设备无关的渲染命令的轻量对象,并将图层树发送到 GPU 线程来渲染到设备上。不要阻塞这个线程!在性能图层的最低栏展示该线程。
GPU 线程取回图层树并通知 GPU 渲染。尽管无法直接与 GPU 线程或其数据通信,但如果该线程变慢,一定是开发者 Dart 代码中的某处导致的。图形库 Skia 在该线程运行,有时也被叫做光栅器 (rasterizer) 线程。在性能图层的最顶栏显示该线程。
可能阻塞 UI 或者 GPU 线程的耗时任务(大多数情况下是 I/O)。该线程并不会在性能图层中展示。
所以,我们做性能优化,关心DartUI,关心GPU两个线程,掉不掉帧,卡不卡的关键,就看这两位了,而且在99%情况下,作为Flutter开发人员,我们我们基本上解决好,DartUI线程上的问题,就==解决了渲染性能问题。
为了更加直观的表示3个树的从生到死,我不得不抛出下面这幅图来
然后,我们经常在做性能调优的时候,会用到timeline工具,你会看到这样一幅图:
现在串起来了吗,4个线程,build---layout---paint三个阶段是不是都一目了然,各发生在什么地方,什么阶段,谁先谁后。
所以,我们说 要解决卡顿掉帧的问题,就是要解决build,layout,paint这三个阶段各函数执行耗时的问题。
首先,我们配置下环境,这里我配置这个变量debugProfileBuildsEnabled=true
不然,我不知道build他具体做了些啥,观望台默认不会告诉我。一般来说,放在main函数中,在runApp之前开启即可,比如我是这么干的:
这里面有一些其他需要用到的开关,可以在数据不足的时候开启,这样我们参考的数据多些,优化的参考点就明确些。
然后,我们执行 flutter run --profile ,请记住,我们需要在profile模式来性能调优,debug模式因为在渲染过程中记录了很多分析数据且加上支持热重载的特性是损失了很多性能为代价的,profile模式更加接近release模式性能。
然后跑起来了,会出现一个链接:
点一下就去了观望台了,当然,你也可以使用devTools,貌似后面会取代观望台。devTools的启动姿势是:
flutter pub global activate devtools
devTools
先安装,然后在直接运行即可:
点击这个链接,会弹出一个网页来,让你输入url,这个url就是我们那个观望台的url,因此你似乎秒懂了,然来,devTools是在观望台的基础至上做的一个分析工具,所以,Google一定是觉得观望台不大友好了,然而,遗憾的是,devTools并不是特别全面,因为现在还是preview阶段嘛,一切都会好起来的。
通常来说,很容易发现有问题的地方,明显那个会比较宽比较长的地方就比较可以,这种一遍就可以定位页面加载比较慢了,然后我们点击向下箭头,把他放大点看看
大概就看到了,偶,然后,我们点击选择,在选择一个范围看看统计效果:
这时候,我们就发现问题了,然后这个也加载了这么多个TipCacheNetWorkImage,然后每个大概要2ms,然后我这个是一个列表页:
所以,一共就有8个这样的控件要渲染,而他,就占用8 *2.188 > 16ms
,因此我们找到了优化点,解决这个就可以加速渲染了,这里只是举例找到存在性能瓶颈的地方,具体相关函数耗时的优化,相信大家都懂的,这就是算法相关的问题了。
1、尽量将setState放在叶子节点,好处是build时影响范围极小,简称局部刷新
2、能不用 Opacity
Widget,就尽量不要用,因为这货会粗发GPU一个saveLayer的指令,做Skia的大神说,这个指令相当耗时。
3、使用ListView.builder()而不是直接使用ListView()来构建列表。
4、对于频繁更新的控件(比如倒计时,秒表),使用RepaintBoundary隔离它,让他在一个独立的paint区域。
5、使用const来修饰永远不需要变更的控件。
6、优先使用StateLessWidget,而不是全部用StateFulWidget
7、使用Visibility控件替换if/else,有些小伙伴喜欢else时return一个 占位控件,须不知,Visibility做法性能更好,至少树结构不会发生改变。
参考资料
调试 Flutter 应用 - Flutter 中文文档 - Flutter 社区中文资源
https://zhuanlan.zhihu.com/p/88478737
https://files.flutter-io.cn/events/gdd2018/Profiling_your_Flutter_Apps.pdf
Flutter 应用性能优化最佳实践 - Flutter 中文文档 - Flutter 社区中文资源
https://medium.com/flutter/managing-visibility-in-flutter-f558588adefe