当前位置: 首页 > article >正文

flutter 专题 八十 Flutter 应用性能检测与优化

概述

软件项目的交付是一个复杂且漫长的过程,任何细小的失误都有可能导致交付过程失败。在软件开发过程中,除了代码逻辑的 Bug 和视觉异常这些功能层面的问题之外,移动应用另一类常见的问题是性能问题,比如滑动操作不流畅、页面出现卡顿丢帧现象等。这些问题虽然不至于让移动应用完全不可用,但也很容易引起用户反感,从而对应用质量产生质疑,甚至失去耐心。

那么,对于应用渲染并不流畅,出现了性能问题,我们该如何检测,又该从哪里着手处理呢?和移动开发类似, Flutter 的性能问题主要可以分为 GPU 线程问题和 UI 线程(CPU)问题两类。对于这些问题,有一个通用的套路:首先,都需要先通过性能图层进行初步分析,而一旦确认问题存在,接下来就是利用 Flutter 提供的各类分析工具来进行问题定位。

图层分析

Flutter运行模式

1、Debug

Debug模式可以在真机和模拟器上同时运行,此模式会打开所有的断言,包括debugging信息、debugger aids(比如observatory)和服务扩展。优化了快速develop/run循环,但是没有优化执行速度、二进制大小和部署。命令flutter run就是以这种模式运行的,通过 sky/tools/gn --android 或者 sky/tools/gn --ios 来构建应用的。

2、Release

Release模式只能在真机上运行,不能在模拟器上运行:会关闭所有断言和debugging信息,关闭所有debugger工具。优化了快速启动、快速执行和减小包体积。禁用所有的debugging aids和服务扩展。这个模式是为了部署给最终的用户使用。命令 flutter run --release 就是以这种模式运行的,通过 sky/tools/gn --android --runtime-mode=release 或者 sky/tools/gn --ios --runtime-mode=release 来构建应用。

3、Profile

Profile模式只能在真机上运行,不能在模拟器上运行,基本和Release模式一致,除了启用了服务扩展和tracing,以及一些为了最低限度支持tracing运行的东西(比如可以连接observatory到进程)。命令 flutter run --profile 就是以这种模式运行的,通过 sky/tools/gn --android --runtime-mode=profile 或者 sky/tools/gn --ios --runtime-mode=profile 来构建应用。

4、test

headless test模式只能在桌面上运行,基本和Debug模式一致,除了是headless的而且你能在桌面运行。命令 flutter test 就是以这种模式运行的,通过 sky/tools/gn 来build。

在实际开发中,应该用到上面所说的四种模式又各自分为两种:一种是未优化的模式,供开发人员调试使用;一种是优化过的模式,供最终的开发人员使用。默认情况下是未优化模式,如果要开启优化模式,build的时候在命令行后面添加–unoptimized参数。

不管是移动开发还是前端开发,对于性能问题分析的思路都是先分析并定位问题,Flutter也不例外,借助Flutter 提供的度量性能工具,我们可以快速定位代码中的性能问题,而性能图层就是帮助我们确认问题影响范围的利器,它类似Android的图层分析工具。

为了使用性能图层,Flutter提供了分析(Profile)模式,与调试代码可以通过模拟器在调试模式下找到代码逻辑 Bug 不同,性能问题需要在发布模式下使用真机进行检测。相比发布(Release)模式而言,调试模式增加了很多额外的检查(比如断言),这些检查可能会耗费很多资源;更重要的是,调试模式使用 JIT (即时编译)模式运行应用,代码执行效率较低。这就使得调试模式运行的应用,无法真实反映出它的性能问题。

而另一方面,模拟器使用的指令集为 x86,而真机使用的指令集是 ARM,由于这两种方式的二进制代码执行行为完全不同,因此模拟器与真机的性能差异较大。一些 x86 指令集擅长的操作模拟器会比真机快,而另一些操作则会比真机慢,这也使得我们无法使用模拟器来评估真机才能出现的性能问题。

为了调试性能问题,我们需要在发布模式的基础之上,为分析工具提供少量必要的应用追踪信息,这就是分析模式。除了一些调试性能问题必须的追踪方法之外,Flutter 应用的分析模式和发布模式的编译和运行是类似的,只是启动参数变成了 profile 而已。我们可以在 Android Studio 中通过菜单栏点击 【Run】-【Profile 】‘main.dart’ 选项启动应用,也可以通过命令行参数 flutter run --profile 运行 Flutter 应用。

渲染问题分析

在完成了应用启动之后,接下来我们就可以利用 Flutter 提供的渲染问题分析工具,即性能图层(Performance Overlay)来分析渲染问题了。性能图层会在当前应用的最上层,以 Flutter 引擎自绘的方式展示 GPU 与 UI 线程的执行图表,而其中每一张图表都代表当前线程最近 300 帧的表现,如果 UI 产生了卡顿(跳帧),这些图表可以帮助我们分析并找到原因,如下图所示。
在这里插入图片描述
上图演示了性能图层的展现样式。其中,GPU 线程的性能情况在上面,UI 线程的情况显示在下面,蓝色垂直的线条表示已执行的正常帧,绿色的线条代表的是当前帧。

同时,为了保持 60Hz 的刷新频率,GPU 线程与 UI 线程中执行每一帧耗费的时间都应该小于 16ms(1/60 秒)。在这其中有一帧处理时间过长,就会导致界面卡顿,图表中就会展示出一个红色竖条,如下图所示。
在这里插入图片描述
如果红色竖条出现在 GPU 线程图表,意味着渲染的图形太复杂,导致无法快速渲染;而如果是出现在了 UI 线程图表,则表示 Dart 代码消耗了大量资源,需要优化代码执行时间。

GPU问题定位

GPU渲染问题主要集中在底层渲染耗时上,有时候 Widget 树虽然构造起来容易,但在 GPU 线程下的渲染却很耗时。例如,涉及 Widget 裁剪、蒙层这类多视图叠加渲染,或是由于缺少缓存导致静态图像的反复绘制,都会明显拖慢 GPU 的渲染速度。

接下来,使用性能图层提供的两项参数,即检查多视图叠加的视图渲染开关 checkerboardOffscreenLayers和检查缓存的图像开关checkerboardRasterCacheImages来检查这两种情况。

checkerboardOffscreenLayers

多视图叠加通常会用到 Canvas 里的 savaLayer 方法,这个方法在实现一些特定的效果(比如半透明)时非常有用,但由于其底层实现会在 GPU 渲染上涉及多图层的反复绘制,因此会带来较大的性能问题。

对于 saveLayer 方法使用情况的检查,我们只需要在 MaterialApp 的初始化方法中,将 checkerboardOffscreenLayers 开关设置为 true,分析工具就会自动帮我们检测多视图叠加的情况。使用了 saveLayer 的 Widget 会自动显示为棋盘格式,并随着页面刷新而闪烁。不过,saveLayer 是一个较为底层的绘制方法,因此我们一般不会直接使用它,而是会通过一些功能性 Widget,在涉及需要剪切或半透明蒙层的场景中间接地使用。所以一旦遇到这种情况,我们需要思考一下是否一定要这么做,能不能通过其他方式来实现呢?

比如下面的例子中,我们使用 CupertinoPageScaffold 与 CupertinoNavigationBar 实现了一个动态模糊的效果,代码如下:


CupertinoPageScaffold(
  navigationBar: CupertinoNavigationBar(),//动态模糊导航栏
    child: ListView.builder(
      itemCount: 100,
      //为列表创建100个不同颜色的RowItem
      itemBuilder: (context, index)=>TabRowItem(
            index: index,
            lastItem: index == 100 - 1,
            color: colorItems[index],//设置不同的颜色
            colorName: colorNameItems[index],
          )
    )
);

其中,动态模糊的NavigationBar效果如下图所示。
在这里插入图片描述
当我们开启checkerboardOffscreenLayers之后,可以看到视图蒙层效果对GPU的渲染压力导致性能视图频繁闪动。如果我们没有对动态模糊效果有特殊需求,则可以使用不带模糊效果的 Scaffold 和白色的 AppBar 实现同样的产品功能,来解决这个性能问题。


Scaffold(
  //使用普通的白色AppBar
  appBar: AppBar(title: Text('Home', style: TextStyle(color:Colors.black),),backgroundColor: Colors.white),
  body: ListView.builder(
      itemCount: 100,
      //为列表创建100个不同颜色的RowItem
      itemBuilder: (context, index)=>TabRowItem(
        index: index,
        lastItem: index == 100 - 1,
        color: colorItems[index],//设置不同的颜色
        colorName: colorNameItems[index],
      )
  ),
);

运行一下代码,可以看到,在去掉了动态模糊效果之后,GPU 的渲染压力得到了缓解,checkerboardOffscreenLayers 检测图层也不再频繁闪烁了。


http://www.kler.cn/a/567566.html

相关文章:

  • AUTOSAR整体架构与应用层详解和综合实例
  • 基于fast-whisper模型的语音识别工具的设计与实现
  • 【西瓜书《机器学习》七八九章内容通俗理解】
  • JWT概念及JAVA使用
  • Winbox5怎样设置上网
  • PAT乙级(1026 程序运行时间)C语言超详细解析
  • 使用Spring Boot与达梦数据库(DM)进行多数据源配置及MyBatis Plus集成
  • seacms v9 实现的MySQL注入
  • Kubernetes 详解笔记
  • Word快速替换修改学术论文所有中的中括号引用未上标格式
  • BiliBili视频下载-原理与实现Python+FFmpeg
  • 【Netty】五种经典 IO 模型详解,附各种模型流程图及流程详解
  • 特征值与特征向量
  • 【GraphQL API 漏洞简介】
  • vue3+TS使用i18n.global.locale,页面要刷新时才更新
  • 第十四届蓝桥杯:DFS之飞机降落
  • 如何在VUE框架下渲染出来一个水球图
  • 建筑兔零基础人工智能自学记录34|深度学习与神经网络2
  • AI时代保护自己的隐私
  • 【多模态大模型】阶跃星辰放大招Step-Audio:统一语音理解与生成的智能语音交互模型