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

news/2025/3/4 6:35:33/

概述

软件项目的交付是一个复杂且漫长的过程,任何细小的失误都有可能导致交付过程失败。在软件开发过程中,除了代码逻辑的 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个不同颜色的RowItemitemBuilder: (context, index)=>TabRowItem(index: index,lastItem: index == 100 - 1,color: colorItems[index],//设置不同的颜色colorName: colorNameItems[index],))
);

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


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

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


http://www.ppmy.cn/news/1576479.html

相关文章

算法学习笔记:169. 多数元素——摩尔投票算法(Moore‘s Voting Algorithm)

摩尔投票算法 摩尔投票算法最早由 Robert S. Boyer 和 J Strother Moore 在 1981 年的论文 “MJRTY—A Fast Majority Vote Algorithm” 中提出。这篇论文描述了摩尔投票算法的原理和证明,并展示了它在实际应用中的高效性。 论文的引用信息如下: Title: …

(七)消息队列-Kafka 序列化avro(传递)

(七)消息队列-Kafka 序列化avro(传递) 客从远方来,遗我双鲤鱼。呼儿烹鲤鱼,中有尺素书。 ——佚名《饮马长城窟行》 本文已同步CSDN、掘金平台、知乎等多个平台,图片依然保持最初发布的水印&…

基于 ‌MySQL 数据库‌对三级视图(用户视图、DBA视图、内部视图)的详细解释

基于 ‌MySQL 数据库‌对三级视图(用户视图、DBA视图、内部视图)的详细解释,结合理论与实际操作说明: 一、三级视图核心概念 数据库的三级视图是 ANSI/SPARC 体系结构的核心思想,MySQL 的实现逻辑如下: …

0301 leetcode - 1502.判断是否能形成等差数列、 682.棒球比赛、657.机器人能否返回原点

1502.判断是否能形成等差数列 题目 给你一个数字数组 arr 。 如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。 如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false…

Spring Boot 使用过滤器filter

执行流程 在Spring Boot项目中,过滤器(Filter)的执行流程遵循Servlet规范。具体来说,过滤器是在请求到达目标资源之前和响应返回给客户端之前执行的一系列操作。下面是详细的过滤器执行流程: 初始化阶段: 当Web应用启…

SQL Server2019安装步骤+使用+解决部分报错+卸载(超详细 附下载链接)

1、下载安装SQL Server2019 第一步:官网下载安装包SQL Server 2019 - 定价 | Microsoft 【以下内容图片借用SQL Server2019安装步骤(超详细 附下载链接) - 掘金中内容】 第二步:打开安装包,并选择基本. 第三步&#…

三次握手内部实现原理

socket()创建一个新的套接字 int socket(int domain, int type, int protocol); 参数: domain:地址族,如 AF_INET(IPv4),AF_INET6(IPv6) type:套接字类型&…

【CSS—前端快速入门】CSS 常用样式

CSS 常用 CSS 样式 1. 前端样式查询网站: MDN Web Docs (mozilla.org) w3school 2. border 2.1 借助 MDN 了解 border 我们借助 MDN 网站来学习 border 样式的使用: 2.2 border 常见属性 保存代码,打开页面: 对于标签不同样式的…