Flutter 07 框架和三棵树(Widgets、Elements和RenderObjects)

一、Flutter框架的整体结构:

Flutter是Google推出并开源的跨平台开发框架,主打跨平台、高保真、高性能。开发者可以通过Dart语 言开发Flutter应用,一套代码同时运行在ios和Android平台。不仅如此,Flutter还支持Web、桌面、嵌 入应用的开发。Flutter提供了丰富的组件、接口,开发者可以很快地为Flutter添加native扩展。同时 Flutter还使用skia引擎渲染视图,这无疑能为用户提供良好的体验。 下面来看一下Flutter框架的整体结构组成:

Flutter主要有三个主要组成部分:框架层、引擎层、平台层。

框架层:

Flutter框架建立在Dart语言的基础上:

Foundation:Framework的最底层叫Foundation,其中定义的大都是非常基础的、提供给其他所 有层使用的工具类和方法;

Animation:动画相关的类库;

Painting:绘制库(Painting)封装了Flutter Engine提供的绘制接口,主要是为了在绘制控制等固 定样式的图形时提供更直观、更方便的接口,比如绘制缩放后的位图、绘制文本、插值生成阴影以 及在盒子周围绘制边框等等;

Gesture:提供了手势识别相关的功能,包括触摸事件类定义和多种内置的手势识别器;

Widgets:在Flutter中一切UI皆widget,Flutter有两大不同风格的widget库:

        1)一个是基于Material Design(材料设计)风格的组件库;

        2)一个是基于cupertino的ios设计风格的组件库。 

引擎层:

Flutter引擎使用的是基于c++的2D图形库(称为Skia)。在这一层中,提供了Dart VM,以提供一个执 行环境,用于将Dart代码转换为本地平台可执行代码。Flutter引擎在Android、ios中运行,以为widget 呈现对应的外观,并根据特定平台通过Channel进行通信;

平台层:

Flutter根据不同平台提供了其特定的shell(既Android Shell和IOS Shell),这些shell用来托管Dart VM,以提供对特定的平台API的访问;

二、Flutter绘制原理:

熟悉Flutter绘制原理有助于我们了解Flutter框架的原理机制。为了熟悉Flutter绘制原理,我们先从屏幕 显示图像的基本原理开始说起:

我们在买显示器时,都会关注显示器的刷新频率;那么对于手机屏幕也是一样的,通常手机屏幕的刷新 频率是60Hz,当然现在也有不少高刷新频率的手机也在推出,如:90Hz,120Hz。

一般来说,计算机系统中,CPU、GPU和显示器以一种特定的方式协作:CPU将计算好的显示内容提交 给GPU,GPU渲染后放放帧缓冲区,然后视频控制器按照VSync信号从帧缓冲区取帧数据传递给显示器 显示。当一帧图像绘制完毕后准备绘制下一帧时,显示器就会发出一个垂直同步信号(VSync),所以 60Hz的屏幕就会一秒内发生60次这样的信号。

上面是CPU、GPU和显示器协作方式,对于Flutter也不例外,Flutter也遵循了这种模式: 

GPU的VSync信号同步给到UI线程,UI线程使用Dart来构建抽象的视图结构,这份数据结构在GPU线程 进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。

三、Android UI绘制原理浅析:

在上面Flutter绘制原理的阐述中提到最终交由Skia引擎来进行图形渲染,听到这个词是不是可以联想到 我们的Android呢?所以这里转一个视角,对Android UI的绘制原理进行一个简单回顾:

说到Android的UI绘制自然离不了Canvas,Android上层的UI绘制几乎都通过Canvas来完成的,那么 Canvas又是怎么完成UI绘制的呢,接下来就让我们来通过追踪源码来一探究竟,下面以Canvas绘制圆 形这个API来例进行分析:

Canvas.java:drawCircle

其中它的父类是:

BaseCanvas.java:drawCircle  

BaseCanvas.java:nDrawCircle

此时就进入了c++的世界了。

Canvas.cpp:drawCircle

SkCanvas.h:drawCircle 

由此可以看出Android UI绘制最终还是交给Skia来完成的。

四、Flutter渲染流程:

在Flutter框架中存在着一个渲染流程(Rendering pipline)。这个渲染流水线是由垂直同步信号 (Vsync)驱动的,而Vsync信号是由系统提供的,如果你的Flutter app是运行在Android上的话,那 Vsync信号就是我们熟悉的Android那个Vsync信号。

当Vsync信号到来以后,Fluttter框架会按照图里的顺序执行一系列动作:

1.动画(Animate)

2.构建(Build)

3.布局(Layout)

4.绘制(Paint)

最终生成一个场景(Scene)之后送往底层,由GPU绘制到屏幕上。 

1、动画(Animate)阶段:因为动画会随每个Vsync信号的到来而改变状态(State),所以动画阶段 是流水线的第一个阶段;

2、构建(Build)在这个阶段Flutter,在这个阶段那些需要被重新构建的Widget会在此时被重新构 建。也就是我们熟悉的StatelessWidget.build()或者State.build()被调用的时候;

3、布局(Layout)阶段:这时会确定各个显示元素的位置,尺寸;此时是 RenderObject.performLayout()被调用的时候;

4、绘制(Paint)阶段:此时是RenderObject.paint()被调用的时候;

以上是整个渲染流程的一个大致的工作过程。

五、Flutter组件的生命周期:

createState():当框架要创建一个StatefulWidget时,它会立即调用State的createState();

initState():当State的构造方法被执行后,会调用一次initState(),需要指出的是initState()在State 生命周期内只被调用一次;

build():这个方法会被经常调用,比如:setState以及配置改变都会触发build()方法的调用;

didUpdateConfig():当收到一个新的config时调用;

setState():当需要修改页面状态,比如刷新数据等的时候我们可以通过调用setState来实现;

dispose():当移除State对象时,将调用dispose();通常在该方法中进行取消订阅,取消所有动画 ,流等操作; 

六、Flutter渲染机制之三棵树:

Flutter是一个优秀的UI框架,借助它开箱即用的Widgets我们能够构建出漂亮和高性能的用户界面。那 这些Widgets到底是如何工作的又是如何完成渲染的。 所以接下来就来探析Widgets背后的故事-Flutter渲染机制之三棵树。

什么是三棵树?

在Flutter中和Widgets一起协同工作的还有另外两个伙伴:Elements和RenderObjects;由于它们都是有着树形结构,所以经常会称它们为三棵树。

1)Widget:Widget是Flutter的核心部分,是用户界面的不可变描述。做Flutter开发接触最多的就是 Widget,可以说Widget撑起了Flutter的半边天;

2)Element:Element是实例化的 Widget 对象,通过 Widget 的 createElement() 方法,是在特定位 置使用 Widget配置数据生成;

3)RenderObject:用于应用界面的布局和绘制,保存了元素的大小,布局等信息; 

初次运行时的三棵树:

初步认识了三棵树之后,那Flutter是如何创建布局的?以及三棵树之间他们是如何协同的呢?接下来就 让我们通过一个简单的例子来剖析下它们内在的协同关系:

class ThreeTree extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container(color: Colors.red,child: Container(color: Colors.blue));}
}

上面这个例子很简单,它由三个Widget组成:ThreeTree、Container、Text。那么当Flutter的 runApp()方法被调用时会发生什么呢?下面在Flutter工程中先来构建这么一个简单的示例:

运行一下:

此时可以打开“Flutter Inspector”:

那第二棵树在哪里呢?此时需要跟一下源码了:

总结一下就是:

当runApp()被调用时,第一时间会在后台发生以下事件:

1)Flutter会构建包含这三个Widget的Widgets树;

2)Flutter遍历Widget树,然后根据其中的Widget调用createElement()来创建相应的Element对象, 最后将这些对象组建成Element树;

3)接下来会创建第三个树,这个树中包含了与Widget对应的Element通过createRenderObject()创建 的RenderObject;

而整个状态过程可以用下图来描述: 

从图中可以看出Flutter创建了三个不同的树,一个对应着Widget,一个对应着Element,一个对应着 RenderObject。每一个Element中都有着相对应的Widget和RenderObject的引用。可以说Element是存在于可变Widget树和不可变RenderObject树之间的桥梁。Element擅长比较两个Object,在Flutter里面就是Widget和RenderObject。它的作用是配置好Widget在树中的位置,并且保持对于相对应的 RenderObject和Widget的引用。 

三棵树的作用:

那这三棵树有啥意义呢?简而言之是为了性能,为了复用Element从而减少频繁创建和销毁 RenderObject。因为实例化一个RenderObject的成本是很高的,频繁的实例化和销毁RenderObject对 性能的影响比较大,所以当Widget树改变的时候,Flutter使用Element树来比较新的Widget树和原来的 Widget树,接下来从源码中来体会一下:

此时也是只更新对应的element,接下来继续:

总结如下:

1)如果某一个位置的Widget和新Widget不一致,才需要重新创建Element;

2)如果某一个位置的Widget和新Widget一致时(两个widget相等或runtimeType与key相等),则只需要修改RenderObject的配置,不用进行耗费性能的RenderObject的实例化工作了;

3)因为Widget是非常轻量级的,实例化耗费的性能很少,所以它是描述APP的状态(也就是configuration)的最好工具;

4)重量级的RenderObject(创建十分耗费性能)则需要尽可能少的创建,并尽可能的复用;

因为在框架中,Element是被抽离开来的,所以你不需要经常和它们打交道。每个Widget的build (BuildContext context)方法中传递的context就是实现了BuildContext接口的Element。

更新时的三棵树:

那如果此时我们修改一下程序:

因为Widget是不可变的,当某个Widget的配置改变的时候,整个Widget树都需要被重建。例如当我们改变一个Container的颜色为橙色的时候,框架就会触发一个重建整个Widget树的动作。因为有了Element的存在,Flutter会比较 新的Widget树中的第一个Widget和之前的Widget。接下来比较Widget 树中第二个Widget和之前Widget,以此类推,直到Widget树比较完成。

Flutter遵循一个最基本的原则:判断新的Widget和老的Widget是否是同一个类型:

1)如果不是同一个类型,那就把Widget、Element、RenderObject分别从它们的树(包括它们的子 树)上移除,然后创建新的对象;

2)如果是一个类型,那就仅仅修改RenderObject中的配置,然后继续向下遍历;

在我们的例子中,ThreeTree Widget是和原来一样的类型,它的配置也是和原来的ThreeTreeRender一 样的,所以什么都不会发生。下一个节点在Widget树中是Container Widget,它的类型和原来是一样 的,但是它的颜色变化了,所以RenderObject的配置也会发生对应的变化,然后它会重新渲染,其他的 对象都保持不变。

上面这个过程是非常快的,因为Widget的不变性和轻量级使得他能快速的创建,这个过程中那些重量级 的RenderObject则是保持不变的,直到与其相对应类型的Widget从Widget树中被移除。 注意这三个树,配置发生改变之后,Element和RenderObject实例没有发生变化。

当Widget的类型发生改变时:

和刚才流程一样,Flutter会从新Widget树的顶端向下遍历,与原有树中的Widget类型进行对比。

因为FlatButton的类型与Element树中相对应位置的Element的类型不同,Flutter将会从各自的树上删除 这个Element和相对应的ContainerRender,然后Flutter将会重建与FlatButton相对应的Element和 RenderObject。如下:

很明显这个重新创建的过程相对耗时的,但是当新的RenderObject树被重建后将会计算布局,然后绘制 在屏幕上面。Flutter内部使用了很多优化方法和缓存策略来处理,所以你不需要手动来处理这些。以上便是Flutter的整体渲染机制,可以看出Flutter利用了三棵树很巧妙的解决的性能的问题。 

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

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

相关文章

【工具】OCR方法|不用下载额外的软件,提取扫描中英文PDF的目录文本的最优解!(一)

需求: 1)从PDF里快速提取目录; 2)不想下载任何软件。 我提取出来的目录文本会用于嵌入到PDF中,向PDF批量添加目录的软件以及软件的使用方法可以看我上一篇文章:PDF批量插入目录。 以下是我自己能想到的方…

基于yolov2网络的人脸识别系统matlab仿真,包括识别正脸,侧脸等

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、训练阶段 4.2、预处理阶段 4.3、识别阶段 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ........................................…

FastGPT | 3分钟构建属于自己的AI智能助手

这是一篇使用指南!!! FastGPT是什么? FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景&…

BEV-YOLO 论文学习

1. 解决了什么问题? 出于安全和导航的目的,自驾感知系统需要全面而迅速地理解周围的环境。目前主流的研究方向有两个:第一种传感器融合方案整合激光雷达、相机和毫米波雷达,和第二种纯视觉方案。传感器融合方案的感知表现鲁棒&am…

一句话说明:企业架构框架鼻祖Zachman

问:禁止废话,一句话表达,Zachman是什么?包含哪些内容? 韩老师正经回答:Zachman是企业架构框架鼻祖,包含6行6列的矩阵式架构内容。6列是5W1H(What、How、Where、Who、When、Why&…

Vue3 简单实现虚拟Table,展示海量单词.利用WebAPI speechSynthesis,朗读英语单词

目录 本页面完整代码 视频演示 完整的页面代码 利用webapi speechSynthesis帮助我们自动郎读英语单词,可以利用这个API,做一些小说朗读或到账提示。 本页面完整代码 用Vue写了一个简单页面,里面还写了一个简单的虚拟Table支持海量数据展示…

kubernetes存储-volumes

目录 一、Volumes的简介 二、emptyDir卷 1、emptyDir的引入 2、emptyDir 的使用场景 3、多容器共享volumes 4、emptyDir缺点 三、hostPath卷 1、hostPath卷简介 2、创建hostPath卷 3、NFS共享文件 四、PersistentVolume(持久卷) 1、PV与P…

免费音效素材,不能错过这6个网站

找免费音效素材,那必须要上这6个网站,热门音效、BGM都能免费下载,赶紧收藏起来。 1、菜鸟图库 https://www.sucai999.com/audio.html?vNTYwNDUx 菜鸟图库是一个综合性素材网站,站内涵盖设计、图片、办公、视频、音效等素材。其中…

数据可视化:动态柱状图

终于来到最后一个数据可视化的文章拿啦~~~ 在这里学习如何绘制动态柱状图 我先整个活 (๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤ 什么是pyecharts? 答: Python的Pyecharts软件包。它是一个用于Python数据可视化和图表绘制的库,可用于制作…

力扣最热一百题——盛水最多的容器

终于又来了。我的算法记录的文章已经很久没有更新了。为什么呢? 这段时间都在更新有关python的文章,有对python感兴趣的朋友可以在主页找到。 但是这也并不是主要的原因 在10月5号我发布了我的第一篇博客,大家也可以看见我的每一篇算法博客…

【23真题】易!题源全部定位!带讲解!

今天分享的是23年长春理工大学808的信号与系统试题及解析。 本套试卷难度分析:22年长春理工808考研真题,我也发布过,若有需要,戳这里自取!本套试题内容难度中等偏下,题量较少,没有选择填空题&a…

算法学习打卡day41|栈和队列:栈和队列相互实现、括号匹配、逆波兰表达式、滑动窗口最大值问题、求前 K 个高频元素

栈和队列相互实现 力扣题目链接:用栈实现队列、用队列实现栈 题目描述: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty): 实现 MyQueue 类: void push(…

无人机航迹规划:狐猴优化算法LO求解无人机路径规划MATLAB(可以修改起始点,地图可自动生成)

一、狐猴优化算法 狐猴优化算法(Lemurs Optimizer,LO)由Ammar Kamal Abasi等人于2022年提出,该算法模拟狐猴的跳跃和跳舞行为,具有结构简单,思路新颖,搜索速度快等优势。狐猴优化算法&#xff…

JavaScript 进阶问题列表,巩固自己的知识。

不定时更新 JavaScript 进阶问题列表 从基础到进阶,测试你有多了解 JavaScript,刷新你的知识,或者帮助你的 coding 面试! 💪 🚀 答案❤️ 1. 输出是什么? function sayHi() {console.log(na…

iSlide2024一款基于PPT的插件工具包含38个设计辅助功能

根据使用者情况表明iSlide 是一款拥有30W素材的PPT高效设计软件,可提高90%工作效率,现全球已有超过1400万使用者,智能排版原创高品模板可商用图形,真正摆脱PPT的束缚,把精力用在该用的地方。我们都明白islide插件功能特…

Vue 3 中,watch 和 watchEffect 的区别

结论先行: watch:需要指明要监听的数据,而且在回调函数中可以获取到属性变化的前后值; 适用于需要精确控制监视范围的情况;也就是需要针对特定数据变化执行操作。 watchEffect:不用指明监听哪个属性&…

python单元测试框架(继承、unittest参数化、断言、测试报告)

一、继承 继承能解决什么问题? unittest每个模块都要用到前提条件以及清理,如果有上百个模块,我们要改域名和浏览器,就会工作量很大特别麻烦,这时我们可以用继承的思想只用改一次 我们可以将前提和清理提出来单独放…

新登录接口独立版变现宝升级版知识付费小程序-多领域素材资源知识变现营销系统

源码简介: 资源入口 点击进入 源码亲测无bug,含前后端源码,非线传,修复最新登录接口 梦想贩卖机升级版,变现宝吸取了资源变现类产品的很多优点,摒弃了那些无关紧要的东西,使本产品在运营和变现…

MVC、MVP、MVVM区别

MVC、MVP、MVVM区别 MVC(Model-View-Controller) 。是一种设计模式,通常用于组织与应用程序的数据流。它通常包括三个组件:模型(Model)、视图(View)和控制器(Controller&…

TDengine 上榜 BenchCouncil 全球首个开源贡献榜

近日,Bench Council(国际测试委员会)公布了“世界首个开源贡献榜”,该榜单号称“只以贡献分高下”。值得一提的是,涛思数据、TDengine 上榜 BenchCouncil 发布的开源计算机系统机构榜、成果榜,TDengine 创始…