前言

在熟悉了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框架之旅,敬請期待。