这篇文章主要介绍“分析Flutter应用性能检测与优化”,在日常操作中,相信很多人在分析Flutter应用性能检测与优化问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分析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 检测图层也不再频繁闪烁了。
checkerboardRasterCacheImages
从资源的角度看,另一类非常消耗性能的操作是渲染图像,因为图像渲染会涉及 I/O、GPU 存储以及不同通道的数据格式转换,因此渲染过程的构建需要消耗大量资源。为了缓解 GPU 的压力,Flutter 提供了多层次的缓存快照,这样 Widget 重建时就无需重新绘制静态图像了。
与检查多视图叠加渲染的 checkerboardOffscreenLayers 参数类似,Flutter 提供了检查缓存图像的开关 checkerboardRasterCacheImages,来检测在界面重绘时频繁闪烁的图像。
为了提高静态图像显示性能,我们可以把需要静态缓存的图像加到 RepaintBoundary 中,RepaintBoundary 可以确定 Widget 树的重绘边界,如果图像足够复杂,Flutter 引擎会自动将其缓存,从而避免重复刷新。当然,因为缓存资源有限,如果引擎认为图像不够复杂,也可能会忽略 RepaintBoundary。下面的代码展示了通过 RepaintBoundary,将一个静态复合 Widget 加入缓存的具体用法,如下所示。
RepaintBoundary(//设置静态缓存图像 child: Center( child: Container( color: Colors.black, height: 10.0, width: 10.0, ), ));
UI 线程问题定位
如果说 GPU 线程问题定位的是渲染引擎底层渲染异常,那么 UI 线程问题发现的则是应用的性能瓶颈。比如在视图构建时,在 build 方法中使用了一些复杂的运算,或是在主 Isolate 中进行了同步的 I/O 操作。这些问题,都会明显增加 CPU 的处理时间,拖慢应用的响应速度。
针对这类问题,我们可以使用 Flutter 提供的 Performance 工具,来记录应用的执行轨迹。Performance 是一个强大的性能分析工具,能够以时间轴的方式展示 CPU 的调用栈和执行时间,去检查代码中可疑的方法调用。
打开 Android Studio 底部工具栏中的“Open DevTools”按钮之后,系统会自动打开 Dart DevTools 的网页,将顶部的 tab 切换到 Performance 后,我们就可以开始分析代码中的性能问题了。
接下来,我们通过一个在 ListView 中计算 MD5 的例子来演示 Performance 的具体分析过程。考虑到在 build 函数中进行渲染信息的组装是一个常见的操作,为了演示Performance的使用过程,我们故意放大计算 MD5 的耗时,如循环迭代计算了 1 万次。
class MyHomePage extends StatelessWidget { MyHomePage({Key key}) : super(key: key); String generateMd5(String data) { //MD5固定算法 var content = new Utf8Encoder().convert(data); var digest = md5.convert(content); return hex.encode(digest.bytes); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('demo')), body: ListView.builder( itemCount: 30,// 列表元素个数 itemBuilder: (context, index) { //反复迭代计算MD5 String str = '1234567890abcdefghijklmnopqrstuvwxyz'; for(int i = 0;i<10000;i++) { str = generateMd5(str); } return ListTile(title: Text("Index : $index"), subtitle: Text(str)); }// 列表项创建方法 ), ); } }
与性能图层能够自动记录应用执行情况不同,使用 Performance 来分析代码执行轨迹,我们需要手动点击【Record】按钮去主动触发,在完成信息的抽样采集后再点击【Stop】按钮结束录制,然后就可以得到在这期间应用的执行情况了。
Performance 记录的应用执行情况叫做 CPU 帧图,又被称为火焰图。火焰图是基于记录代码执行结果所产生的图片,用来展示 CPU 的调用栈,表示的是 CPU 的繁忙程度。所以,我们要检测 CPU 耗时问题,皆可以查看火焰图底部的哪个函数占据的宽度最大。只要有“平顶”,就表示该函数可能存在性能问题,如下图所示。
可以看到,_MyHomePage.generateMd5 函数的执行时间最长,几乎占满了整个火焰图的宽,而这也与代码中存在的问题是一致的。在找到了问题之后,我们就可以使用 Isolate(或 compute)将这些耗时的操作挪到并发主 Isolate 之外去完成了。
到此,关于“分析Flutter应用性能检测与优化”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!