前言
在熟悉了Flutter app开发以后,我们的好奇心会驱使对Flutter框架是如何运行产生诸多疑问,Flutter是如何运转的?Widget
到底是什么东西?RenderObject
又是个什么鬼?runApp()
之后发生了什么?调用sateState()
之后页面又是如何刷新的?要解答这些问题,就需要学习一下Flutter框架的源代码。为此我会基于源码写一系列文章来分析一下Flutter框架。本文是第一篇,主要是先介绍一下Flutter框架的总览和基础--Window
。
总览
Flutter app的页面是如何显示到屏幕上的呢?是什么驱动Flutter app刷新界面,播放动画以及响应触摸事件呢?这一过程可以用下图来描述。
rendering pipline在Flutter框架中存在着一个渲染流水线(Rendering pipline)。这个渲染流水线是由垂直同步信号(Vsync)驱动的,而Vsync信号是由系统提供的,如果你的Flutter app是运行在Android上的话,那Vsync信号就是我们熟悉的Android的那个Vsync信号。当Vsync信号到来以后,Flutter 框架会按照图里的顺序执行一系列动作: 动画(Animate)、构建(Build)、布局(Layout)和绘制(Paint),最终生成一个场景(Scene)之后送往底层,由GPU绘制到屏幕上。
- 动画(Animate)阶段:因为动画会随每个Vsync信号的到来而改变状态(State),所以动画阶段是流水线的第一个阶段。
- 构建(Build)在这个阶段Flutter,在这个阶段那些需要被重新构建的Widget会在此时被重新构建。也就是我们熟悉的
StatelessWidget.build()
或者State.build()
被调用的时候。 - 布局(Layout)阶段,这时会确定各个显示元素的位置,尺寸。此时是
RenderObject.performLayout()
被调用的时候。 - 绘制(Paint)阶段,此时是
RenderObject.paint()
被调用的时候。
以上是整个渲染流水线的一个大致的工作过程。
Flutter app只有在状态发生变化的时候需要触发渲染流水线。当你的app什么都不做的时候是不需要重新渲染页面的。所以,Vsync信号需要Flutter app去调度。比如我们都知道如果你的某个页面需要发生变化的时候有可能会调用State.setState()
,这个调用Flutter框架最终会发起一个调度Vsync信号的请求给底层。然后底层会在Vsync信号到来的时候驱动渲染流水线开始运作,最后把新的页面显示到屏幕上。
Flutter整体架构如下图所示:
Flutter架构可见整个Flutter架构是分为两部分的。上层的框架(Framework)部分和底层的引擎(Engine)部分。
- 框架(Framework)部分是用Dart语言写的,也是本系列文章主要涉及的部分。
- 引擎(Engine)部分是用C++实现的。引擎为框架提供支撑,也是连接框架和系统(Android/iOS)的桥梁。
触发渲染流水线的Vsync信号是来自引擎,渲染完成以后的场景也是要送入引擎来显示,并且Vsync信号的调度也是框架通过引擎来通知系统的。渲染流程从框架和引擎交互的角度用一个示意图来表示就是下面这个样子:
渲染调度示意图
- 框架通知引擎(
scheduleFrame
)需要调度一帧。 - 在系统的Vsync信号到来以后,引擎会首先会回调框架的
_beginFrame
函数。此时框架的渲染流水线进入动画(Animate)阶段, - 在动画(Animate)阶段阶段完成以后。引擎会处理完微任务队列,接着再回调框架的
_drawFrame
函数。渲染流水线继续按序运行构建、布局和绘制。 - 绘制结束以后,框架调用
render
将绘制完成的场景送入引擎以显示到屏幕上。
在前端开发中我们都会对于用户界面有一个窗口(Window)的概念,我们写的程序的UI都是容纳在窗口中的,窗口是框架的根基。界面的绘制,用户输入的事件的处理等等都是要通过窗口来管理。Flutter也不例外。上述框架和引擎渲染交互流程也是统一纳入窗口管理的。所以要了解Flutter框架,首先得从Flutter的窗口开始。
Window
Flutter中的Window
来自库dart:ui
。相关源代码在window.dart
中。
首先,在Flutter中,Window
是个单例:
/// The [Window] singleton. This object exposes the size of the display, the
/// core scheduler API, the input event callback, the graphics drawing API, and
/// other such core services.
final Window window = new Window._();
Window
单例对上层提供屏幕尺寸,调度接口,输入事件回调,图形绘制接口以及其他一些核心服务。总体来说,window
集中提供了Flutter引擎中和图形界面相关的接口。
Window
中和渲染流水线相关的api如下:
// vcync信号到来以后的回调
FrameCallback _onBeginFrame;
VoidCallback _onDrawFrame;
// 请求engine调度一帧
void scheduleFrame() native 'Window_scheduleFrame';
// 绘制完成后将场景送入engine显示
void render(Scene scene) native 'Window_render';
大家注意一下函数名称后面的native
关键字,表明这个函数是调用到engine层的。和Android中的jni调用类似。
除渲染相关的API,window
中还有一些其他重要的API也列一下:
//触摸事件的回调
PointerDataPacketCallback _onPointerDataPacket;
// 获取启动时初始页面的路由
String _defaultRouteName() native 'Window_defaultRouteName';
// 发送PlatfromMessage。这个是Platform channels机制的一部分
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';
//收到platform message后的回调
PlatformMessageCallback _onPlatformMessage;
还有一些和locale,accessbility的相关的API就不列出来了。
总结
至此,Flutter的Window
就大概给大家介绍完了。可见Window
其实并不复杂,基本上只是对engine层对上提供的和用户界面相关的接口的封装。Flutter框架是基于Window
建立起来的。如果你愿意的话,完全可以基于Window
搭建起自己的一套框架来取代Flutter :)。在了解了Flutter的渲染流水线和窗口这个基础设施之后。接下来我们会以此为基础,开始奇妙的Flutter框架之旅,敬请期待。
网友评论