当前位置: 首页 > news >正文

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利用了三棵树很巧妙的解决的性能的问题。 

相关文章:

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

一、Flutter框架的整体结构: Flutter是Google推出并开源的跨平台开发框架,主打跨平台、高保真、高性能。开发者可以通过Dart语 言开发Flutter应用,一套代码同时运行在ios和Android平台。不仅如此,Flutter还支持Web、桌面、嵌 入应…...

EasyExcel 导出冻结指定行

导出的实体类 package org.jeecg.modules.eis.test;import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.*; import lombok.Getter; import lombok.Setter; import org.apache.poi.ss.usermodel.HorizontalAlignment;import…...

ke9案例三:页面提交文件,我服务器端接收

案例三:页面提交文件,我服务器端接收 ProcessFile.java 1value "/process-file" 2获取邮件消息的所有部分part--Collection<Part> partsrequest.getParts(); 3遍历每一个part 4之后可以打印头文件等String headerpart.getHeader("content-disposition&q…...

springboot调用第三方接口json转换成对象

请求接口是一个比较常见的需求&#xff0c;接口返回一般是一个json类型&#xff0c;需要进行组装成对应的类&#xff0c;例 {"status_code": 200,"message": "success","data": {"cost": 286.6933,"bom_list": […...

uniapp使用vue3和ts开发小程序自定义tab栏,实现自定义凸出tabbar效果

要实现自定义的tabbar效果&#xff0c;可以使用自定义tab覆盖主tab来实现&#xff0c;当程序启动或者从后台显示在前台时隐藏自带的tab来实现。自定义一个tab组件&#xff0c;然后在里面实现自定义的逻辑。 组件中所使用的组件api可以看&#xff1a;Tabbar 底部导航栏 | uView…...

麒麟信安获批牵头成立国家关键领域信创行业产教融合共同体

日前&#xff0c;由麒麟信安、长沙理工大学、长沙职业技术学院联合牵头成立的国家关键领域信创行业产教融合共同体&#xff08;以下简称&#xff1a;共同体&#xff09;已获湖南省教育厅批准&#xff0c;并推荐至教育部。 目前共同体已吸引10余家联盟单位及全国20余家企业、高…...

好消息,微信消费者投诉工具升级,可以直接回复用户、处理投诉了。。。

大家好&#xff0c;我是小悟 兄弟们&#xff0c;阅读本文之前&#xff0c;建议先阅读【连夜干出来一个自动处理【微信消费者投诉管理系统】&#xff0c;支持多商户】。 为了使工具更好用&#xff0c;也为帮助商户更好地处理消费者投诉&#xff0c;提升用户满意度&#xff0c;…...

手动修复 rabbitmq 报错 “Crash dump is being written to“

rabbitmq 报错: 2023-11-07 16:38:52.682 [error] emulator Error in process <0.368.0> on node rabbitrabbitmq-0.rabbitmq-discovery.openstack.svc.cluster.local with exit value: {shutdown,[{mnesia_loader,handle_exit,2,[{file,"mnesia_loader.erl"}…...

日志门面技术

1.JCL public abstract class LogFactory {public static Log getLog(Class clazz) throws LogConfigurationException {// 默认实现类为LogFactoryImplreturn getFactory().getInstance(clazz);} }利用LogFactoryImpl实例化具体的日志框架。其中&#xff0c;如果存在log4j依赖…...

机器人制作开源方案 | 管内检测维护机器人

一、作品简介 作者&#xff1a;李泽彬&#xff0c;李晋晟&#xff0c;杜张坤&#xff0c;禹馨雅 单位&#xff1a;运城学院 指导老师&#xff1a;薛晓峰 随着我国的社会主义市场经济的飞速发展和科学技术的革新&#xff0c;各行各业的发展越来越离不开信息化和网络化的…...

k8s存储卷

目录 1、emptyDir存储卷 2、hostPath存储卷 3、nfs共享存储卷 4、PVC 和 PV 4.1 PV和PVC之间的相互作用遵循这个生命周期&#xff1a; 4.2 PV的状态 4.3 一个PV从创建到销毁的具体流程如下&#xff1a; 静态PVC&#xff1a; 动态PVC 1、emptyDir存储卷 当Pod被分配给节…...

View 自定义 - 属性 xml

一、概念 在 xml 中为控件设置的属性。自定义属性名称如果使用系统已定义的&#xff0c;例如 textSize 会在编译时报错。 格式类型定义/使用 string 字符串 <attr name "myContent" format "color" /> android:myContent "Hello Word!&quo…...

2007-2022年全国各地级市金融机构网点数据

2007-2022年地级市金融机构网点数据 1、时间&#xff1a;2007-2022年 2、指标&#xff1a;行政区划代码、年份、城市名称、所属省份、银行网点数量、其中-政策性银行及国家开发银行营业网点占比、其中-商业银行营业网点数量占比、其中-农村金融机构营业网点数量占比 3、范围…...

OpenAI开发者大会掀起风暴:GPT模型价格狂降50%,应用商店即将亮相,AI技术将引爆全球!

OpenAI首届开发者大会召开了&#xff01; 关键信息&#xff1a; GPT-4升级版GPT-4 Turbo来了&#xff0c;上下文窗口达到128k&#xff0c;为GPT-4的4倍&#xff1b;OpenAI还降低了几乎所有模型的API使用价格&#xff0c;整体便宜了一半多&#xff1b;GPT-4系列的多模态能力向B…...

yo!这里是STL::unordered系列简单模拟实现

目录 前言 相关概念介绍 哈希概念 哈希冲突与哈希函数 闭散列 框架 核心函数 开散列 框架 核心函数 哈希表&#xff08;开散列&#xff09;的修改 迭代器实现 细节修改 unordered系列封装 后记 前言 我们之前了解过map和set知道&#xff0c;map、set的底层结构是…...

基础课25——业务流程分析

1.流程的定义&作用 业务流程是企业中一系列创造价值的活动的组合&#xff0c;它是企业运营的基础&#xff0c;也是企业提高效率、优化资源配置的重要手段。通过优化业务流程&#xff0c;企业可以更好地满足客户需求&#xff0c;提高客户满意度&#xff0c;同时也可以提高自…...

快速实现一个企业级域名 SSL 证书有效期监控巡检系统

Why 现在对于企业来说&#xff0c;HTTPS 已经不是可选项&#xff0c;已经成为一个必选项。HTTPS 协议采用 SSL 协议&#xff0c;采用公开密钥的技术&#xff0c;提供了一套 TCP/IP 传输层数据加密的机制。SSL 证书是一种遵守 SSL 协议的服务器数字证书&#xff0c;一般是由权威…...

[SSD综述 1.5] SSD 主控和固件核心功能详解(万字)

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 1. 主控概述1.1 主控作用 2. 主控的硬件功能和实现2.1 主控处理器2.2 闪存、主机接口2.3 主控纠错2.4 断电保护 3 固件功能3.1 FTL3.2 预留空间(Over-provisioning)3.3 Trim3.4 写入放大(Write amplification)3.5 …...

Mybatis-Plus前后端分离多表联查模糊查询分页

数据准备 数据库配置: /*Navicat Premium Data TransferSource Server : localhost_3306Source Server Type : MySQLSource Server Version : 80100 (8.1.0)Source Host : localhost:3306Source Schema : test01Target Server Type : MySQLT…...

【Ruoyi管理后台】用户登录强制修改密码

近期有个需求&#xff0c;就是需要调整Ruoyi管理后台&#xff1a;用户如果三个月(长时间)未修改过密码&#xff0c;需要在登录时强制修改密码&#xff0c;否则不能登录系统。 一、后端项目调整 从需求来看&#xff0c;我们需要在用户表增加一个字段&#xff0c;用于标记用户最…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...