专栏首页玩转全栈Flutter卡顿优化锦辑
原创

Flutter卡顿优化锦辑

 Flutter卡顿优化必备基础知识

首先,在做性能调优之前,我们应该对flutter相关基础知识有一定的了解,不然我们无从做起,首先,我们要了解flutter是干嘛的--Flutter 是谷歌2018年发布的跨平台移动UI框架

然后,他相对于其他跨平台开发框架来说,是高效的,至于他为什么高效,原因是因为:

他是直接调用Skia框架,而其他框架需要借助现有的原始框架来转一下,在才开始去调用Skia框架,这一来二去,肯定就有所消耗,这是其一,然后,我们还需要知道flutter的四个线程。

 四个线程

分别是:

平台线程

该平台的主线程。插件代码在这里运行。更多信息请参阅:iOS 的程序 (UIKit) 文档,或者 Android 的主线程 (MainThread) 文档。性能图层并不会展示该线程。

 DartUI 线程

UI 线程在 Dart VM 执行 Dart 代码。该线程包括开发者写下的代码和 Flutter 框架根据应用行为生成的代码。当应用创建和展示场景的时候,UI 线程首先建立一个 图层树(layer tree) ,一个包含设备无关的渲染命令的轻量对象,并将图层树发送到 GPU 线程来渲染到设备上。不要阻塞这个线程!在性能图层的最低栏展示该线程。

 GPU 线程

GPU 线程取回图层树并通知 GPU 渲染。尽管无法直接与 GPU 线程或其数据通信,但如果该线程变慢,一定是开发者 Dart 代码中的某处导致的。图形库 Skia 在该线程运行,有时也被叫做光栅器 (rasterizer) 线程。在性能图层的最顶栏显示该线程。

 I/O 线程

可能阻塞 UI 或者 GPU 线程的耗时任务(大多数情况下是 I/O)。该线程并不会在性能图层中展示。

所以,我们做性能优化,关心DartUI,关心GPU两个线程,掉不掉帧,卡不卡的关键,就看这两位了,而且在99%情况下,作为Flutter开发人员,我们我们基本上解决好,DartUI线程上的问题,就==解决了渲染性能问题。

三棵树

  • Widget是为Element描述需要的配置, 负责创建Element,决定Element是否需要更新。Flutter Framework通过差分算法比对Widget树前后的变化,决定Element的State是否改变。当重建Widget树后并未发生改变, 则Element不会触发重绘,则就是Widget树的重建并不一定会触发Element树的重建。
  • Element表示Widget配置树的特定位置的一个实例,同时持有Widget和RenderObject,负责管理Widget配置和RenderObject渲染。Element状态由Flutter Framework管理, 开发人员只需更改Widget即可。
  • RenderObject表示渲染树的一个对象,负责真正的渲染工作,比如测量大小、位置、绘制等都由RenderObject完成。

为了更加直观的表示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阶段嘛,一切都会好起来的。

好的,假如,我们的app有性能问题,我们就会打开观望台,然后打开timeLine,点击Flutter Develop,然后在你觉得有问题的页面多操作记下,然后点击右上角Refresh按钮,就会出现:

通常来说,很容易发现有问题的地方,明显那个会比较宽比较长的地方就比较可以,这种一遍就可以定位页面加载比较慢了,然后我们点击向下箭头,把他放大点看看

大概就看到了,偶,然后,我们点击选择,在选择一个范围看看统计效果:

这时候,我们就发现问题了,然后这个也加载了这么多个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://mrale.ph/dartvm/

https://medium.com/flutter/managing-visibility-in-flutter-f558588adefe

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【译】Profiling Flutter Applications Using the Timeline

    使用Timeline可以查找和解决应用程序中的特定性能问题。它也是一个很好的工具,可以识别出Flutter所提供的所有特性的相对性能成本,并允许您做出更明智的决...

    brzhang
  • 小程序怎么将视觉搞实现成ui

    这个真的是强烈建议,对于一个页面,首先应该大致的看一下可以划分为哪几个模块,通常的划分方式是,整体来看,是row排列,还是column排列的,那么,最擅长做这种...

    brzhang
  • flutter接入现有的app详细介绍

    接入的方式,我是参考的官方的介绍文档,我这里尝试的是android的接入方式,还算比较顺利。

    brzhang
  • Java线程池异常处理的正确姿势

    假设我们有一个线程池,由于程序需要,我们向该线程池中提交了好多好多任务,但是 这些任务都没有对异常进行try catch处理,并且运行的时候都抛出了异常 。这会...

    黄泽杰
  • Java线程池「异常处理」正确姿势:有病就得治

    假设我们有一个线程池,由于程序需要,我们向该线程池中提交了好多好多任务,但是 这些任务都没有对异常进行try catch处理,并且运行的时候都抛出了异常 。这会...

    xjjdog
  • Netty中的线程处理EventLoop

    运行任务处理的在编程上的构造通常称作事件循环,Netty使用EventLoop来描述。一个EventLoop将由一个永远不会变的Thread驱动,它可以被指派给...

    爬蜥
  • 使用Redis单实例实现分布式锁(代码)

    在同一个jvm进程中时,可以使用JUC提供的一些锁来解决多个线程竞争同一个共享资源时候的线程安全问题,但是当多个不同机器上的不同jvm进程共同竞争同一个共享资源...

    java思维导图
  • 为什么局部变量是线程安全的?

    当多个线程访问add方法的时候 并操作add方法下的变量,永远都不会导致数据竞争,为什么呢? look at the next line↓:

    奕仁
  • 005.多线程-线程的生命周期

    新建状态: 当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。 如:ThreadTest thread2 = new ThreadT...

    qubianzhong
  • JMeter(连载3)

    这个组件用于测试流程的参数化,参数化文件采用类似于CSV文件。如图16所示。通过菜单“Add->Config Element->CSVData Set Conf...

    小老鼠

扫码关注云+社区

领取腾讯云代金券