首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter性能优化

Flutter性能优化

作者头像
码客说
发布2020-08-11 15:34:43
2.2K0
发布2020-08-11 15:34:43
举报
文章被收录于专栏:码客码客

前言

App 流畅性的关键指标有 UI帧率,GPU帧率,我们期望它能达到 60fps,也就是16ms每帧。

以 profile / release 模式运行

为了获取最接近生产环境的数据,我们应该选择一台尽可能低端的真机,并且以 profile 模式或者 release 模式下运行app。

  1. 因为 debug 模式会有一些额外的检查工作,比如assert()
  2. 为了加速开发效率,debug 模式是以 JIT(Just in time)模式编译 dart 代码的, 而 profilerelease 是提前编译为机器码 AOT(Ahead Of Time),所以 debug 会慢很多。

所以说我们在查看性能时候不要用debug 模式,之前我就是用debug模式,无论怎么优化,性能都满足不了要求,还以为是flutter自身的问题,但是都说Flutter的渲染效率还是很高的,原来是debug模式的问题。

Flutter运行模式

  • Debug模式 调试页面开发时使用
  • Profile模式 调试性能 开发时使用
  • Release模式 部署发包时使用

Debug

Debug模式可以在真机和模拟器上同时运行,此模式会打开所有的断言,包括debugging信息、debugger aids(比如observatory)和服务扩展。优化了快速develop/run循环,但是没有优化执行速度、二进制大小和部署。

命令flutter run就是以这种模式运行的,通过sky/tools/gn --android或者sky/tools/gn --ios来构建应用的。

Release

Release模式只能在真机上运行,不能在模拟器上运行:会关闭所有断言和debugging信息,关闭所有debugger工具。优化了快速启动、快速执行和减小包体积。禁用所有的debugging aids和服务扩展。这个模式是为了部署给最终的用户使用。

命令flutter run --release就是以这种模式运行的,通过sky/tools/gn --android --runtime-mode=release或者sky/tools/gn --ios --runtime-mode=release来构建应用。

Profile

Profile模式只能在真机上运行,不能在模拟器上运行,基本和Release模式一致,除了启用了服务扩展和tracing,以及一些为了最低限度支持tracing运行的东西(比如可以连接observatory到进程)。

命令flutter run --profile就是以这种模式运行的,通过sky/tools/gn --android --runtime-mode=profile或者sky/tools/gn --ios --runtime-mode=profile来构建应用。

test

headless test模式只能在桌面上运行,基本和Debug模式一致,除了是headless的而且你能在桌面运行。

命令flutter test就是以这种模式运行的,通过sky/tools/gn来build。

怎么使用profile模式呢?

为了调试性能问题,我们需要在发布模式的基础之上,为分析工具提供少量必要的应用追踪信息,这就是分析模式。

除了一些调试性能问题必须的追踪方法之外,Flutter 应用的分析模式和发布模式的编译和运行是类似的,只是启动参数变成了 profile 而已。

我们可以在 Android Studio 中通过菜单栏点击 Run=>Profile=>main.dart 选项启动应用,

也可以通过命令行参数 flutter run --profile 运行 Flutter 应用。

Android Studio

菜单栏点击 Run => Profile...

注意

该过程第一次编译非常慢请耐心等待,后来就会快很多。

VSCode

打开 launch.json 文件并设置flutterModeprofile

"configurations": [
  {
    "name": "Flutter",
    "request": "launch",
    "type": "dart",
    "flutterMode": "profile" # 测试完后记得把它改回去!
  }
]

命令行

flutter run --profile

检测帧率

Android Studio中配置

File=>Settings中搜索flutter找到

image-20200810225801166
image-20200810225801166

打开Open Flutter Inspector view on app launch

image-20200810225532436
image-20200810225532436

选中 View > Tool Windows > Flutter Performance .

第一个按钮会在应用中显示,最后按钮一个会减速,方便我们查看帧率

VS Code中配置

选中 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个主要线程分别承担了什么职责。

  • Platform线程:插件代码运行的线程;即Android/iOS的主线程,
  • UI线程:在Dart虚拟机中执行Dart代码。作用是创建视图树,然后将它发送给GPU。注意不要阻塞此线程!
  • GPU线程:把上面提到的视图树渲染出来,虽然我们在flutter中不能直接访问GPU线程和数据,但是Dart代码可能导致此线程变慢
  • I/O线程:执行比较耗时的任务

在运行app的过程中,观察爆红的地方和触发场景,进行分析。

如果是UI报红:

那么可能是执行了某个较耗时的函数?或者函数调用过多?算法复杂度高?

如果只是 GPU 报红:

那么可能是要绘制的图形过于复杂?或者执行了过多GPU操作?

  • 比如要实现一个混合图层的半透明效果:如果把透明度设置在顶层控件上,CPU会把每个子控件图层渲染出来,再执行saveLayer操作保存为一个图层,最后给这个图层设置透明度。而saveLayer开销很大,这里官方给出了一个建议:首先确认这些效果是否真的有必要;如果有必要,我们可以把透明度设置到每个子控件上,而不是父控件。裁剪操作也是类似。
  • 还有一个拖慢GPU渲染速度的是没有给静态图像做缓存,导致每次build都会重新绘制。我们可以把静态图形加到RepaintBoundry控件中,引擎会自动判断图像是否复杂到需要用repaint boundary,不需要的话也会忽略。
  • 开启saveLayer和图形缓存的检查 MaterialApp( showPerformanceOverlay: true, // 使用了saveLayer的图形会显示为棋盘格式并随着页面刷新而闪烁 checkerboardOffscreenLayers: true, // 做了缓存的静态图片在刷新页面时不会改变棋盘格的颜色;如果棋盘格颜色变了说明被重新缓存了,这是我们要避免的 checkerboardRasterCacheImages: true, );

提高流畅性的策略

  • 代码调用时机是否可以延后?如底部导航栏式的页面,没有必要第一次进入就把每个子Page都创建出来
  • 尽量做到局部刷新
  • 把耗时的计算放到独立的isolate去执行
  • 检查不必要的 saveLayer
  • 检查静态图片是否添加缓存
  • relayout boundary:参考
  • repaint boundary:参考

内存优化

在内存优化方面,我们的目标是希望减少应用内存占用,减少被系统杀死的概率,同时尽可能的避免内存泄露,减少内存碎片化。

内存优化策略

  • 加载对象过大?如图片质量和尺寸不做限制就加载
  • 加载对象过多?如加载长列表;在调用频率很高的方法中创建对象
    • 合理设置缓存大小/长度
    • 在内存不足时或离开页面时清空缓存数据
    • 使用ListView.build()来复用子控件
    • 自定义绘图中避免在onDraw中做创建对象操作,或者相同的参数设置
    • 复用系统提供的资源,比如字符串、图片、动画、样式、颜色、简单布局,在应用中直接引用
  • 内存泄露的问题?比如dispose需要销毁的listener等
  • 不可见的视图是否也在build?
  • 页面离开后的网络请求是否取消?
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Flutter运行模式
    • Debug
      • Release
        • Profile
          • test
          • 怎么使用profile模式呢?
            • Android Studio
              • VSCode
                • 命令行
                • 检测帧率
                  • Android Studio中配置
                    • VS Code中配置
                      • 代码中配置
                      • 怎么看帧率
                      • 提高流畅性的策略
                      • 内存优化
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档