通过一系列文章记录一下学习 Flutter 的过程,总结一下相关知识点。
- 学习Flutter -- 框架总览
- 学习Flutter -- 启动过程做了什么
- 学习Flutter -- Widget 的组成
- 学习Flutter -- Element 的作用
- 学习Flutter -- RenderObject 布局过程
- 学习Flutter -- RenderObject 绘制过程
本文先对 Flutter 的架构做一个整体的介绍,为的是能够对 Flutter 有一个全面的认识和整体的印象。对后续深入学习 Flutter 的细节非常重要,不至于迷茫所接触的内容属于哪一部分的,并且也能够更好的规划学习路线。
一、Flutter 框架结构
首先看一下 Flutter 官方提供的 Flutter 框架图,如图:
Flutter 的整体架构可分为三层:框架层、引擎层、嵌入层,每层都是建立在前一层之上。
-
框架层 (Framework)
Framework,即框架层。在使用 Flutter 开发过程中,我们直接接触的就是 Flutter Framework,它是纯 Dart 实现的 SDK,实现了一套基础库供我们开发直接使用,从底向上简单介绍一下:
-
Foundation & Animation、Painting、Gesture
可简称 Dart UI 层,对应的是 Flutter 中的 dart:ui 库,是 Flutter Engine 底层暴露的 UI 库, 提供动画、绘制、手势等能力。
-
Rendering
渲染层, 依赖于 Dart UI 层,它是 Framework 中最核心的部分。渲染层会构建一棵由可渲染对象(RenderObject)组成的渲染树,当动态更新这些渲染对象时,渲染树会找出变化的部分,重新更新渲染。
渲染层不仅能够确定渲染对象的位置、大小,还会进行坐标的变换、绘制(调用 dart:ui)。
-
Widgets
Widgets 组件层是 Flutter 提供的一套基础组件库,同时在它之上 Flutter 还提供了 Material 和 Cupertino 两种风格的组件库,分别实现了 Material 和 iOS 的设计规范。
-
引擎层 (Engine)
Engine, 引擎层。主要是由 C/C++ 语言实现,引擎为框架层提供了支撑,也是连接框架层和宿主操作系统的桥梁,当代码中调用 dart:ui 库时,调用最终会走到引擎层,从而实现真正的绘制和现实。包含的能力有:
-
Skia ,Google 的一个 2D 渲染引擎,是跨平台的
-
Dart 运行时(即 Dart runtime)
-
Text 文字排版引擎
-
...
-
-
嵌入层 (Embedder)
Embedder,即嵌入层。嵌入层主要是将 Flutter 引擎 “安装” 到指定平台上,Flutter 最终的渲染、交互是要依赖其所在平台操作系统的 API 的。主要能力有:
-
Flutter 代码通过嵌入层,以模块的方式集成到现有应用中,也可以作为应用的主体。
-
嵌入层是使用当前平台的语言编写的,如 Android 使用的是 Java 和 C++,iOS 使用的是 Objective-C 和 Objective-C++,Windows 和 Linux 使用的是 C++。Flutter 若要支持新的平台,只需要针对平台写个嵌入层即可快速适配。
-
以上就是 Flutter 框架的一个整体介绍。为方便理解概括如图所示:
二、渲染流水线
-
帧(frame)的概念
一次绘制的过程,称为一帧。
FPS
我们通常说的 FPS (Frame Per Second)用于描述屏幕的刷新频率,比如 60 fps,是指一秒内最多可触发 60 次的绘制,FPS 越大,屏幕刷新是次数就越多,界面也就越流畅。
Flutter 中的 Frame
但是在此要强调的是,接下来在介绍 Flutter 框架的过程中,提到的 frame 的概念并不等同屏幕刷新帧(frame),因为 Flutter 框架中 frame 的绘制并不是每次屏幕刷新都会触发,只有当 UI 需要发生变化时,才会重新走渲染流程。
因此,我们接下来提到 frame 时,并不是和屏幕刷新频率对应的 frame。
-
调度过程
Flutter 应用的生命周期可以用两种状态来概括:idle 和 frame。idle 表示空闲的,没有 frame 需要处理。当状态发生改变需要刷新 UI 时,会去请求新的 frame,之后会变成 frame 状态。整个整个生命周期就是在 idle 和 frame 两种状态之间切换。
概要如图所示:
-
渲染流水线
我们知道 Flutter 是有着自己独立的渲染引擎的,那么Flutter app 的页面是如何显示到屏幕上的呢?是什么驱动 Flutter app进行页面的刷新以及响应事件的呢?其实,在 Flutter 框架中存在这一个渲染流水线(Rendering Pipeline)的,如图所示:
这个渲染流水线是Vsync(垂直同步信号)信号驱动着,Vsync是由系统产生的。当 Vsync 信号到来后,Flutter 框架按照上图所示的顺序进行一系列动作:
-
Animate 动画
动画作为渲染流水线的第一个阶段,因为随着每个 Vsync 信号的到来,动画会首先进行状态(State) 的改变Build 构建,从而后续绘制出新的页面。
-
Build 构建
随着状态改变之后,widgets 在这个阶段会被重新构建,也就是调用到我们熟悉的 build() 方法。
-
Layout 布局
布局阶段会确定渲染对象的位置、尺寸等,即调用 RenderObject.performeLayout() 的过程。
-
Paint 绘制
最终会绘制成一系列的图层(layer),即调用 RenderObject.paint() 的过程。
完成绘制后会生成一个图层树(Layer Tree),最终图层树中的绘制信息会提交给 Flutter engine 进行真正的渲染。
以上就是Flutter 渲染流水线的一个大致过程。
当你的app 什么都不做的时候是不需要重新渲染页面,只有当状态发生改变的时候才需要重新触发渲染流水线。所以,Vsync 信号产生的起源还是需要Flutter app 来触发。当我们需要更新页面时候会调用 setState() 方法,之后 Flutter 框架会向底层发起调度一个 Vsync 信号,当 Flutter 框架收到新的 Vsync 信号开始触发渲染流水线,最后把新的页面绘制到屏幕上。
概括如图所示:
三、总结
- Flutter 的整体架构可分为三层:框架层、引擎层、嵌入层;
- Flutter 中存在一条由于 Vsync 信号驱动的渲染流水线,主要过程有:动画(Animate)、构建(Build)、布局(Layout)和绘制(Paint);
- Flutter 中首帧绘制完成后,只有当 UI 需要变化的时候才会触发重绘流程。