一、Flutter架构

家喻户晓,Flutter是由Google推出的开源的高性能跨平台框架,一个2D渲染引擎。在Flutter中,Widget是Flutter用户界面的根本形成单元,能够说所有皆Widget。与Weex和RN框架应用的JsCore转化的中间层不同,Flutter采纳的是全新的架构计划,领有本人的渲染引擎和Dart下层,每一层都建设在前一层的根底之上,并且下层比上层的应用频率更高,其框架架构如下图所示。

能够看到,自下而上,Flutter分为Embedder、Engine和Framework三层。其中,Embedder是操作系统适配层,次要负责Surface渲染设置,线程设置,以及平台插件等平台相干个性的适配;Engine层负责图形绘制、文字排版和提供Dart运行时,Engine层具备独立虚拟机,正是因为它的存在,Flutter程序能力运行在不同的平台上,实现跨平台运行;Framework层则是应用Dart编写的一套根底视图库,蕴含了动画、图形绘制和手势辨认等性能,是应用频率最高的一层。

  • Flutter Embedder:Embedder是Flutter的操作系统适配层,又称为嵌入层,通过该层能够把Flutter嵌入到各个不同的平台下来。Embedder的次要工作包含Surface渲染设置、线程设置、事件循环以及插件的平台适配等。
  • Flutter Engine:纯 C++实现的 SDK,其中包含 Skia引擎、Dart运行时、文字排版引擎等。它是 Dart的一个运行时,它能够以 JIT 或者 AOT的模式运行 Dart代码。这个运行时还管制着 VSync信号的传递、GPU数据的填充等,并且还负责把客户端的事件传递到运行时中的代码。
  • Flutter Framework:纯 Dart实现的 SDK,提供了一整套自底向上的根底库, 用于解决动画、绘图和手势。并且基于绘图封装了一套 UI组件库,而后依据 Material 和Cupertino两种视觉格调辨别开来。在平时利用开发中,与开发者打交道最多的就是这一层,并且最多的就是各种Widget。

二、渲染流程

不论是什么渲染框架,其根本的原理都是:个别以60Hz的固定频率刷新,每一帧图像绘制实现后,会持续绘制下一帧,而后显示器就会收回一个Vsync信号,按60Hz计算,屏幕每秒会收回60次这样的信号。CPU计算好显示内容提交给GPU,GPU渲染好交给显示器显示。

在Flutter中,渲染会用到很多的线程,次要是UI线程和GPU线程,下图是Flutter App线程的运作原理图。

上面重点看一下UI线程和GPU线程。

UI Task Runner

UI Task Runner用于执行Root Isolate代码,它运行在线程对应平台的线程上,属于子线程。同时,Root isolate在引擎启动时会绑定不少Flutter须要的函数办法,这些绑定的函数能够提交渲染帧给Engine层执行渲染操作,下图演示了Widgets生成Layer Tree的过程。

对于每一帧,引擎通过Root Isolate告诉Flutter Engine有帧须要渲染,平台收到Flutter Engine告诉后会创建对象和组件并生成一个Layer Tree,而后将生成的Layer Tree提交给Flutter Engine。此时,只生成了须要绘制的内容,并没有执行屏幕渲染,而Root Isolate就是负责将创立的Layer Tree绘制到屏幕上,因而如果线程过载会导致卡顿掉帧景象。

除了用于解决渲染之外,Root Isolate还须要解决来自Native Plugins的音讯响应、Timers、MicroTasks和异步IO。如果的确有无奈防止的沉重计算,倡议将这些耗时的操作放到独立的Isolate去执行,从而防止利用UI卡顿问题。

GPU Task Runner

GPU Task Runner用于执行设施GPU指令,UI Task Runner创立的Layer Tree是跨平台的。也就是说,Layer Tree提供了绘制所须要的信息,然而由谁来实现绘制它是不关怀的。

GPU Task Runner的次要责任就是负责将Layer Tree提供的信息转化为平台可执行的GPU指令,同时它也负责管理每一帧绘制所须要的GPU资源,包含平台Framebuffer的创立,Surface生命周期治理,以及Texture和Buffers的绘制机会等,下图GPU Task Runner的工作流程。


UI Runner和GPU Runner运行在不同的线程。GPU Runner会依据目前帧执行的进度去向UI Runner申请下一帧的数据,在工作沉重的时候还可能会呈现UI Runner的提早工作。不过这种调度机制的益处在于,确保GPU Runner不至于过载,同时也防止了UI Runner不必要的资源耗费。

GPU Runner能够导致UI Runner的帧调度的提早,GPU Runner的过载会导致Flutter利用的卡顿,因而在理论应用过程中,倡议为每一个Engine实例都新建一个专用的GPU Runner线程。

三、Widget、Element 和 RenderObject

要了解Flutter的渲染原理,那么就必须理解Widget、RenderObject 和 Element及其作用。总的来说,Flutter调用runApp(rootWidget),将rootWidget传给rootElement,做为rootElement的子节点,生成Element树,由Element树生成Render树,如下图所示。

从下面的介绍中,咱们隐约晓得了Widget、RenderObject 和 Element的作用,简略的介绍一下。

  • Widget:Widget 的次要作用是用来保留 Element 信息的(包含布局、渲染属性、事件响应等信息),自身是不可变的,Element 也是依据 Widget 外面保留的配置信息来治理渲染树,以及决定本身是否须要执行渲染。
  • RenderObject:RenderObject 做为渲染树中的对象存在,次要作用是解决布局、绘制相干的事件,而绘制的内容是Widget传入的内容。
  • Element:Element 能够了解为是其关联的 Widget 的实例,寄存视图构建的上下文数据,能够通过遍历Element来查看视图树,Element同时持有Widget和RenderObject对象。

Flutter通过Widget树中的每个控件创立不同类型的渲染对象,组成渲染对象树,而渲染对象树在Flutter中的展现分为四阶段:布局、绘制、合成及渲染。其中,布局和绘制由RenderObject负责实现,Flutter采纳深度优先机制遍历渲染树对象,确定树中每个对象的地位和尺寸,并把他们绘制到不同的图层上,而合成及渲染则交给Skia实现。

下图展现了Widget、Element 和 RenderObject的关系。

3.1 Widget

在 Flutter 中,万物皆是 Widget,无论是可见的还是功能型的,上面是官网对Widget的介绍。

  • Widget 的作用是用来保留 Element 的配置信息的。
  • Widget 自身是不可变的。
  • Element 依据 Widget 外面保留的配置信息来治理渲染树。
  • Widget 能够屡次的插入到 Widget 树中,每插入一次,Element 都要从新装载一遍 Widget 。
  • Widget 外面的 key 属性用来决定依赖这个 Widget 的 Element 在 Element 树中是更新还是移除。

上面是Widget源码。

abstract class Widget extends DiagnosticableTree{  const Widget({ this.key });  final Key key;    @protected  Element createElement();    static bool canUpdate(Widget oldWidget, Widget newWidget) {   return oldWidget.runtimeType == newWidget.runtimeType        && oldWidget.key == newWidget.key;  }}

Widget有两个重要的办法,一个是通过 createElement 来创立 Element 对象的,一个是依据 key 来决定更新行为的 canUpdate 办法。

3.2 RenderObject

  • RenderObject 是做为渲染树中的对象存在。
  • RenderObject 不定义束缚关系,也就是不会对子控件的布局地位、大小等进行治理。
  • RenderObject 中有一个 parentData 属性,这个属性用来保留其孩子节点的特定信息,如子节点地位,这个属性对其孩子是通明的。

RenderObject的源码如下。

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {  ParentData parentData;  Constraints _constraints;  void layout(Constraints constraints, { bool parentUsesSize = false }) {      }  void paint(PaintingContext context, Offset offset) { }    void performLayout();  void markNeedsPaint() {  }}

能够看出,RenderObject 的次要作用就是绘制和布局。RenderObject 在 Flutter 中的作用分为四个阶段,即布局、绘制、合成和渲染。其中,布局和绘制在 RenderObject 中实现,Flutter 采纳深度优先机制遍历渲染对象树,确定树中各个对象的地位和尺寸,并把它们绘制在不同的图层上。绘制结束后,合成和渲染的工作则交给 Skia 实现。

3.2 Element

  • Element 是关联的Widget 的实例,并且关联在 Widget 树的特定地位上。
  • Widget 是不可变的,一个 Widget 能够同时用来配置多个子 Widget 树,而 Element 就用来代表特定地位的 Widget 。
  • Widget 是不可变的,而 Element 是可变的,Element决定是否须要刷新界面。
  • 一些 Element 只能有一个子节点,如 Container、Opacity、Center ,还有一些能够有多个子节点,如 Column、Row 和 ListView 等。

Element 领有本人的生命周期:

  • Flutter framework 通过 Widget.createElement 来创立一个 Element 。
  • 每当 Widget 创立并插入到 Widget 树中时,framework 就会通过 mount 办法来把这个 widget 创立并关联的 Element 插入到 Element 树中。
  • 通过 attachRenderObject 办法来将 render objects 来关联到 Render 树上,这时能够认为 Widget 曾经显示在屏幕上了。
  • 每当执行了 rebuid 办法,Widget 代表的配置信息扭转时,framewrok 就会调用这个新的 Widget 的 update 办法执行重绘。
  • 当 Element 的先人想要移除一个子 Element 时,能够通过 deactivateChild 办法,先把这个 Element 从 树中移除,而后将这个 Element 退出到一个“不沉闷元素列表”中,接着 framework 就会将这个 element 从屏幕移除。

总的来说,Flutter提出所有皆Widget,Widget 次要用来保留 Element 信息,而Element作用Widget 的实例,寄存视图构建的上下文数据,并且同时持有Widget和RenderObject对象,RenderObject的次要作用是解决布局、绘制相干的事件,确定Element树中每个对象的地位和尺寸。