Flutter性能优化

news2024/11/27 10:42:46

原理

Flutter的架构主要分成三层:Framework,Engine和Embedder

在这里插入图片描述

  • Framework 使用 dart 实现,包括 Material Design 风格的Widget,Cupertino(针对iOS)风格的Widgets,UI/文本/图片/按钮等基础 Widgets,渲染,动画,手势等。此部分的核心代码是: flutter 仓库下的flutter package,以及 sky_engine 仓库下的 io, async, ui (dart:ui 库提供了 Flutter 框架和引擎之间的接口)等package。

  • Engine使用C++实现,主要包括: Skia, Dart 和 Text。

    • Skia是开源的二维图形库,提供了适用于多种软硬件平台的通用API。其已作为Google Chrome,Chrome OS,Android, Mozilla Firefox, Firefox OS等其他众多产品的图形引擎,支持平台还包括Windows, macOS, iOS,Android,Ubuntu等。
    • Dart 部分主要包括:Dart Runtime,Garbage Collection(GC),如果是Debug模式的话,还包括JIT(Just In Time)支持。Release和Profile模式下,是AOT(Ahead Of Time)编译成了原生的arm代码,并不存在JIT部分。
    • Text 即文本渲染,其渲染层次如下:衍生自 Minikin 的 libtxt 库(用于字体选择,分隔行);HartBuzz用于字形选择和成型;Skia作为渲染/GPU后端,在Android和Fuchsia上使用FreeType渲染,在iOS上使用CoreGraphics来渲染字体。
  • Embedder 是一个嵌入层,即把 Flutter 嵌入到各个平台上去,这里做的主要工作包括渲染 Surface 设置,线程设置,以及插件等。从这里可以看出,Flutter 的平台相关层很低,平台(如iOS)只是提

  • 供一个画布,剩余的所有渲染相关的逻辑都在Flutter内部,这就使得它具有了很好的跨端一致性。

Flutter线程

Flutter里面有四个线程分别是Platform Task Runner 、UI Task Runner、GPU Task Runner 和 IO Task Runner。

  • Platform Task Runner:也就是 Android 和 iOS 的主线程
  • UI Task Runner:就是Flutter的 UI 线程
  • GPU Task Runner:GPU线程被用于执行设备GPU的相关调用
  • IO Task Runner:IO线程主要功能是从图片存储(比如磁中读取压缩的图片格式,将图片数据进行处理为GPU Runner的渲染做好准备

主要关注UI线程和GPU线程的性能问题。

Flutter视图树

Flutter视图树包含了三颗树:Widget、Element、RenderObject

  • Widget: 存放渲染内容、它只是一个配置数据结构,创建是非常轻量的,在页面刷新的过程中随时会重建

  • Element: 同时持有Widget和RenderObject,存放上下文信息,通过它来遍历视图树,支撑UI结构
    在这里插入图片描述
    Build函数的参数BuildContext,实际是Element

  • RenderObject: 根据Widget的布局属性进行layout,paint ,负责真正的渲染
    从创建到渲染的大体流程是:根据Widget生成Element,然后创建相应的RenderObject并关联到Element.renderObject属性上,最后再通过RenderObject来完成布局排列和绘制。

从创建到渲染的大体流程是:根据Widget生成Element,然后创建相应的RenderObject并关联到Element.renderObject属性上,最后再通过RenderObject来完成布局排列和绘制。

Flutter运行模式

Flutter有四种运行模式:Debug,Release,Profile,test

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

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

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

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

建议在Profile模式下测试性能,在Android Studio中通过菜单启动Profile模式:
在这里插入图片描述

性能指标

1、流畅度

Github上与流畅度相关的讨论:流畅度

页面帧率

Flutter 在全局 Window 对象上提供了帧回调机制。我们可以在 Window 对象上注册 onReportTimings 方法,将最近绘制帧耗费的时间(即 FrameTiming),以回调的形式告诉我们。

有了每一帧的绘制时间后,我们就可以计算 FPS 了。
为了让 FPS 的计算更加平滑,我们需要保留最近 25 个 FrameTiming 用于求和计算。

由于帧的渲染是依靠 VSync【VSync是垂直同期(Vertical Synchronization)的简称,基本的思路是将你的FPS和显示器的刷新率同期起来。其目的是避免一种称之为"撕裂"的现象。】 信号驱动的,如果帧绘制的时间没有超过 16.67 ms,我们也需要把它当成 16.67 ms 来算,因为绘制完成的帧必须要等到下一次 VSync 信号来了之后才能渲染。而如果帧绘制时间超过了 16.67 ms,则会占用后续 VSync 的信号周期,从而打乱后续的绘制次序,产生卡顿现象。

那么,页面帧率的统计公式就是:
FPS = 60 * 实际渲染的帧数 / 本来应该在这个时间内渲染完成的帧数。

页面加载时长

统计页面可见的时间:WidgetsBinding 提供了单次 Frame 回调的 addPostFrameCallback 方法,它会在当前 Frame 绘制完成之后进行回调,并且只会回调一次。一旦监听到 Frame 绘制完成回调后,我们就可以确认页面已经被渲染出来了,因此我们可以借助这个方法去获取页面的渲染完成时间 endTime。

统计页面创建的时间:获取页面创建的时间比较容易,我们只需要在页面的初始化函数 initState() 里记录页面的创建时间 startTime。

最后,再将这两个时间做减法,你就能得到页面的加载时长。需要注意的是,正常的页面加载时长一般都不应该超过2秒。如果超过了,则意味着有严重的性能问题。

启动速度

2、内存

Github上与内存相关的讨论:内存
能够复用的对象尽量复用,不使用的内存应尽早回收,降低APP内存消耗

3、应用大小

Github上与包大小相关的讨论:应用大小

4、功耗

Github上与功耗相关的讨论:功耗

工具

Flutter DevTools:包含了Inspector和Performance的功能,这两部分功能在下面单独介绍

Flutter Inspector:用于可视化和查看 widget 树

调试布局问题,请在Debug 模式下运行应用程序,然后点击 DevTools 工具栏上的 Flutter inspector 选项打开调试面板。
在这里插入图片描述
主要功能包括:

  • Select Widget Mode:选择 widget 模式,启用此按钮以在设备上选择 widget 进行查看。
  • 刷新树:重新加载当前 widget 的信息。
  • Slow Animations:慢速动画,以五分之一的速度运行动画以便对它们进行优化。
  • Show Guidelines:显示引导线,覆盖一层引导线以帮助调整布局问题。
  • Show Baselines: 显示基线,针对文字对齐展示文字的基线。对检查文字是否对齐有帮助。
  • Highlight Repaints:高亮重绘制内容,重新绘制时在图层上依次显示不同的颜色。
  • Highlight Oversized Images:高亮尺寸过大的图片,在运行的应用程序中高亮并反转消耗过多内存的图像。

参考文档:跳转

Flutter Performance

详细可参考官网文档:使用性能视图

  • Performance Overlay:性能图层用两张图表显示应用的耗时信息。每一张图表都代表当前线程的最近 300 帧表现。点击按钮打开该功能。
    在这里插入图片描述
    PerformanceOverlay 可以视为 Performance Overlay 的高级版本(Performance Overlay 正是基于 PerformanceOverlay 控件来实现的)。
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      showPerformanceOverlay: true,
      title: 'My Awesome App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'My Awesome App'),
    );
  }
}

如果没有使用 MaterialApp 等控件,可以自己调用 PerformanceOverlay.allEnabled(checkerboardOffscreenLayers: true,); 来实现类似效果。

在这里插入图片描述

  • 顶部的图形表示 GPU 线程,即Raster线程所花费的时间:GPU 线程执行 Flutter 引擎中图形相关的代码。某些图层树易于构建却难于渲染,也可能导致这个线程变慢
  • 底部的图表显示了 UI 线程所花费的时间:UI 线程执行 Dart VM 中的 Dart 代码。构建过于复杂的图层树可能导致这个线程变慢
  • 竖轴表示耗时,沿竖轴的黑线是时间线 (间隔单位为 16ms)
  • 横轴则表示帧,垂直的绿色条代表的是当前帧
  • 卡顿时绿色条会变成红色条:如果是在 UI 图表出现了红色竖条,则表明 Dart 代码消耗了大量资源而如果红色竖条是在 GPU 图表出现的,意味着场景太复杂导致无法快速渲染
    在这里插入图片描述

最佳实践

1、

实战

列表页在上下滑动时,从查看性能帧常出现红色条,说明存在性能问题。从现象看,每帧在出现图片时的帧率性能不佳。从而怀疑是图片渲染导致的性能问题。
在这里插入图片描述
列表功能截图如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20d30ae3f5414fc8a0a1e3a9a7951607.png

尝试以下优化方法:
1、

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式学习(一):Bridge桥接模式

一、什么是Bridge模式Bridge模式的作用是在“类的功能层次结构”和“类的实现层次结构”之间搭建桥梁。1.1 类的功能层次结构主要作用就是增加新的功能。当我们要增加新的功能时,我们可以从各个层次的类中找出最符合自己需求的类,然后以它为父类编写子类…

(Week 10)最小生成树(C++,prim,Kruskal,并查集)

文章目录Einstein学画画(C,欧拉路)题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示解题思路:并查集(C)[蓝桥杯 2017 国 C] 合根植物(C,并查集)题目描述输入格…

基于Java+SpringBoot+vue+element实现校园闲置物品交易网站

基于JavaSpringBootvueelement实现校园闲置物品交易网站 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录基于JavaSpri…

Element UI 走马灯的使用

目录 走马灯是什么 原生js实现 Element UI的走马灯使用 el-carousel Carousel Events el-carousel-item 走马灯是什么 在有限空间内,循环播放同一类型的图片、文字等内容,走马灯也叫轮播图。 比如 原生js实现 JS实现轮播图效果(同时…

mysql学习总结二

聚合函数 聚合函数表示对 值的集合 进行操作的 组(集合)函数。 # 华为手机价格的平均值 SELECT AVG(price) FROM products WHERE brand 华为; # 计算所有手机的平均分 SELECT AVG(score) FROM products; # 手机中最低和最高分数 SELECT MAX(score) FR…

SpringBoot解决全局和局部跨域问题的两种方式

前言 在如今前后端分离的开发模式下,跨域是一个非常经典的问题,解决的方式也有很多,比如代理服务器,使用JSONP 我之前也写过一篇解决跨域问题的文章,感兴趣的可以参考:解决Vue前后端跨域问题的多种方式 …

【现代机器人学】学习笔记九:运动规划

本节和前一节 【现代机器人学】学习笔记八:轨迹生成 不同,侧重于避障的内容。有一些我认为的重要的基本的概念:1.路径规划是一个纯几何问题,寻找一条无碰撞路径,不涉及动力学和时间相关内容。因此路径规划是运动规划的…

请查收 | 2022 阿里妈妈技术文章回顾

新年伊始,万象更新!转眼,阿里妈妈技术已陪伴大家走过601天~在此,感谢每位读者朋友的支持与关注回顾2022,我们分享了60篇原创技术文章、发布了1本营销科学系列白皮书、开源了1项向量召回技术方案;阿里妈妈营…

Netty基础入门——NIO【1】

Netty基础入门——NIO【1】 1 NIO 1.1 三大组件 1.1.1 Channel && Buffer Channle channel类似于stream,是读写数据的双向通道,而stream要么是输入要么是输出 #mermaid-svg-9w1vFFYCVQmRvHja {font-family:"trebuchet ms",verdana…

YACC移进规约冲突案例分析

总结 总结: bison给出的用例是发现冲突的最便捷方法。 第一种用例:明确用例(一个Example),直接反应问题。第二种用例:混淆用例(两个Example),解析器无法区分两条语句。…

jenkins 节点部署

1、节点注册 登陆jenkins master界面 路径:首页-->系统管理--> 节点管理-->新建节点(New Node) 插曲:我在新的服务器部署master节点,显示剩余交换空间为0B 处理方式请查看:Jenkins - Free Swap…

关于MCU的BootLoader的一些理解

一、关于STM32单片机IAP升级中if(((*(__IO uint32_t*)Addr_App) & 0x2FFE0000) 0x20000000)语句的理解 参考自:https://blog.csdn.net/weixin_45394120/article/details/122732203?spm1001.2014.3001.5502 疑问: 1、为什么要用Addr_App里的数据…

Web操作系统漏洞发现——工具使用总结

目录 (一)web层面 1、信息收集 0x01 网站源码自己开发 0x02 网站源码使用开源CMS 2、可维护Poc 0x01 pocassist 0x02 afrog 3、APP渗透 0x01 在BP上添加转发端口 0x02 Xray进行监听 0x03 触发数据 4、Goby (二)操作系统层…

xss.haozi靶场通关

做完xss-labs靶场后,再继续做这个靶场,感觉这个不是很难,毕竟在第一个靶场也获取了一些经验,但是这个靶场偏向技巧,所以还是以了解为主。 0x00: 分析:对我们的代码未作出限制,因此这里可以使用…

如何用 nodejs 进行 sha1 加密验证,微信公众号开发验证

如何用 nodejs 进行 sha1 加密验证,微信公众号开发验证 一、问题 今天在摆弄微信公众号的时候,遇到这样一个问题: 我的后台是 nodejs 写的,express 框架,官方开发接入的验证代码是 php 写的,其中有一个部…

C语言之蓝桥杯习题(3)☞暴力求解版(思路写在解题过程中)

第一题.1.问题:小蓝数字卡片题小蓝有很多数字卡片,每张卡片上都是数字0到9。 小蓝准备用这些卡片来拼一些数,他想从1开始拼现在小蓝手里有0到9的卡片各2021张,共20210张,请问小蓝可以从1拼到多少?2.解题过程&#xff…

【Docker】(三)使用registry远程镜像仓库管理镜像

1.前言 本系列文章记录了从0开始学习Docker的过程,Docker系列历史文章: (一)基本概念与安装使用 (二)如何使用Docker发布一个SpringBoot服务 在上一篇中留下了一个问题,使用Docker发布服务的方…

【开源代码 | MATLAB线性阵列仿真】

本文编辑:调皮哥的小助理 1、16阵元均匀线阵方向图 %8阵元均匀线阵方向图,来波方向为0度 clc; clear all; close all; element_num16;%阵元数为16 d_lamda1/2;%阵元间距d与波长lamda的关系 thetalinspace(-pi/2,pi/2,200); theta0[0.2 0.1];%来波方向 w…

systemd wsl 测试笔记

文章目录systemd 简介WSL systemdsystemctljournalctlhello serviceSleep 与 Timeout 测试Requires 测试After 测试systemd 简介 Linux 从关闭到运行, 完整的启动和启动过程有三个主要部分: 硬件启动(Hardware boot): 初始化系统硬件Linux 引导(Linux boot): 加载 Linux 内核&…

基于ERNIELayoutPDFplumber-UIEX的多方案学术论文信息抽取

本项目链接:https://aistudio.baidu.com/aistudio/projectdetail/5196032?contributionType1 0.问题描述 可以参考issue: ERNIE-Layout在(人名和邮箱)信息抽取的诸多问题阐述#4031 ERNIE-Layout因为看到功能比较强大就尝试了一…