找回密码
 会员注册
查看: 21|回复: 0

Flutter性能优化实践之Timeline

[复制链接]

2

主题

0

回帖

7

积分

新手上路

积分
7
发表于 2024-10-11 20:26:51 | 显示全部楼层 |阅读模式
前言Flutter自诞生之时就以轻松构建美观、高性能组件著称,目标是提供逼近“原生性能”的60帧每秒(fps)的性能,或者是在可以达到120Hz的设备上提供120fps的性能。这里的帧率fps是指的画面每秒传输帧数,是衡量性能优化中屏幕是否卡顿的一个重要指标,如何测量一个应用的帧率,就要用到工具Timeline。注:关于Performance性能指标的描述有多个方面,本文侧重点为TimelineFlutter 性能分析flutter 支持三种模式编译的app,处于开发的不同阶段,使用不同模式下的app调试模式在命令行下输入flutter run,默认会启动debug模式,该模式下app使用JIT的编译方式,运行时去解析执行程序,意味着应用有着较慢的性能体验,比如冷启动或者第一次初始化Flutter Engine会有较长时间的黑屏、使用过程中掉帧和卡顿这都属于正常现象。该模式下一个突出的特点是可以热重载,所以更像开发一个前端应用Release模式命令行输入flutter run --release 会使用 Release 模式来进行编译,该模式下应用具有最大的优化和性能体验,采用AOT的编译技术,由dart本地虚拟机将代码编译成对应平台例如Android、IOS对应的机器码,相当于原生开发,编译耗时,失去了热重载,但具有良好性能。一般用于最后应用市场发包,失去了debug模式下的各种调试应用功能Profile模式命令行输入flutter run --profile会使用Profile模式编译,一个专门的调试应用性能模式,该模式是在保留一部分调试能力的基础上,又较大程度还原app真实性能,所以该模式不建议在虚拟机或模拟器上运行,因为无法真实代表真机性能。输入该命令,运行完以后控制台会打印调试的地址点击即可跳转到调试页这里选择timeline,点击Flutter Developer按钮,即可进入timeline的调试页面,在手机上操作几秒钟,点击右上角Refresh按钮,即可加载出图像,看页面代码应该也是临时借用的systemtrace的,操作都类似这个其实是以前旧版的调试方式,现在虽然也能使用,但是实际测试中发现不太准确,使用也不方便,现在基本不使用这种方式了,新版本的Performance页面很美观使用也方便,可以在Android studio中使用Flutter Performance插件中页面粗略判断timeline是否卡顿,也可以打开Flutter Performance右下角Open DevTools按钮在网页上具体分析。Flutter Inspector这里顺便说下Android Studio中其他两个Flutter 插件,一个是Flutter Outline,即显示页面布局的大纲,可以快速查看页面布局的树形结构,菜单栏提供了包裹、删除、上下移动组件的快捷功能,很简单这里不详细介绍。另一个插件是Flutter Inspector,安装Flutter插件后,AS右侧边栏会出现这个标签,主要作用是开发过程中布局调试用的,类似Android开发里的Xml布局查看工具,只不过需要debug模式运行时才可以查看布局,这点相对于原生开发xml快速定位布局文件的体验还是差一些,相信后期还会有好的优化。点击标签后页面如下页面主要分上边功能按钮、左边View树、右边布局预览。每个按钮的作用:「Select Widget Mode」选择组件模式,选中后点击下方View Tree中某个Widget自动定位到代码位置,可在开发中快速定位代码「Refresh Tree」刷新View Tree,在App上跳转其他页面后,View Tree不自动更新,所以有了此按钮「Slow Animations」放慢动画「Debug Paint」显示布局测量,可以快速确定组件边界,效果如下「Show Paint Baselines」显示Text组件的Baseline,方便文字对齐「Show Repaint Rainbow」显示重绘时颜色变化「Invert Oversized Images」轻松查看分辨率比显示分辨率高的图片Flutter Performance点击右边栏Flutter Performance,出现如下页面:左上角第一个按钮是在手机上显示performance Overlay,效果如下,其他按钮和上边Inspector中一样上边可以粗略判断App是否掉帧,白色正常,红色就卡顿了,中间内存占用,下边是每个组件的重绘状态,点击右下角的Open DevTools可以使用更多功能Track widget rebuilds复选框勾上可以方便的查看页面中组件的重绘状态,对于不应该重绘的组件应该调整代码层级结构或者抽离组件的方式避免重绘造成性能的损失,这里分享个人在开发中总结的几点经验:「尽量少使用StatefulWidget编写大的页面,尽量避免在StatefulWidget中使用setState」「不需要重绘组件添加const关键字」「Provider刷新机制时使用Consumer下沉刷新范围」「小部件需要刷新抽取成StatefulWidget,缩小刷新范围」Timeline时间线事件图表显示了应用程序中的所有事件跟踪。Flutter框架在构建框架,绘制场景以及跟踪其他活动(例如HTTP流量)时会发出时间轴事件。这些事件显示在时间轴上。您还可以通过dart发送自己的时间线事件:developer Timeline和 TimelineTask APITimeline 事件轨迹的格式和查看器并被许多其他项目使用,此类项目包括 Chromium & Android (via systrace).轨迹记录的形式是JSON文件格式存储的,点击右上角的Export按钮可以导出文件。打开DevTools以后,在App上操作一段,点击左上角Refresh按钮即可加载出如下图所示时间线。图中「蓝色条是正常帧,红色条是卡顿帧,鼠标移动到红条上可以查看当前卡顿帧的耗时,右上角有不同颜色条的对应关系,分别有UI、Raster、Jank」。UIUI线程在Dart VM中执行Dart代码。这包括您的应用程序以及Flutter框架中的代码。当您的应用创建并显示场景时,UI线程将创建一个层树(包含与设备无关的绘画命令的轻量级对象),并将该层树发送到要在设备上呈现的栅格线程。不要阻塞该线程。Raster光栅线程(以前称为GPU线程)执行Flutter Engine中的图形代码。该线程获取层树并通过与GPU(图形处理单元)对话来显示它。您无法直接访问栅格线程或其数据,但是如果该线程速度很慢,则是由于您在Dart代码中所做的操作所致。图形库Skia在此线程上运行。有时,场景会产生易于构造的图层树,但是在栅格线程上渲染的树代价很高。在这种情况下,您需要弄清楚代码正在做什么,这会导致渲染代码变慢。对于GPU而言,特定种类的工作负载更加困难。它们可能涉及对saveLayer()的不必要调用,与多个对象相交的不透明性以及在特定情况下的剪辑或阴影Jank帧渲染图显示带有红色叠加层的垃圾帧。如果一个帧完成的时间超过约16毫秒(对于60 FPS设备),则该帧被认为是过时的。为了达到60 FPS(每秒帧)的帧渲染速率,每个帧必须在约16 ms或更短的时间内渲染。错过此目标时,您可能会遇到UI混乱或掉帧的情况Render Frames当一个Flutter应用或者Flutter Engine启动时,它会启动(或者从池中选择)另外三个线程,这些线程有些时候会有重合的工作点,但是通常,它们被称为UI线程,GPU线程,IO线程。UI、GPU之间的工作流程如下:为了生成一帧,Flutter engine首先装备了vsync锁存器,一个vsync的事件将会指示Flutter engine开始一些工作并最终绘制出新的帧呈现在屏幕上,vsync事件的生成频率会根据硬件平台的刷新率决定。vsync首先会唤醒UI线程,UI线程的工作是将你代码中编写的Widget树转化为要渲染的RenderTree,Flutter中有三颗树的概念WidgetTree,ElementTree,RenderTree,dart文件中的Widget树并不是最终参与绘制的,而只是方便开发者编写页面的一个配置。比如,我们指定这里有一个纵向列表Column,列表里有三个并列Text,然后Flutter会根据相应语义在对应位置生成对应Element,这才是真正意义上的Flutter UI组件,也是显示到屏幕上的元素。组件树对应到屏幕上还要经过一层渲染树(RenderObject)的转化,RenderObject是实际的渲染对象它负责布局测量以及绘制操作,这样做的目的是为了更好的应对上层UI的频繁变化,尽可能地去比较更新,修改配置而不是直接创建下层树,因为RenderObject树的创建开销比较大,所以Widget重新创建,ElementTree和RenderTree并不会完全重新创建,而是会复用一些节点,提升性能。UI线程工作到生成RenderTree的过程叫做渲染树一旦创建了渲染树,GPU线程就会被唤醒,这个线程的工作是将渲染树的信息转换到GPU的命令缓冲区,然后在同一线程将数据提交给GPU执行示例「模拟一个组件耗时操作」classPageOneextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnContainer(color:Colors.tealAccent,child:Center(childistView(children:[for(vari=0;i
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-27 15:13 , Processed in 0.884671 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表