衆所周知,Flutter是由Google推出的開源的高性能跨平臺框架,一個2D渲染引擎。在Flutter中,Widget是Flutter用戶界面的基本構成單元,能夠說一切皆Widget。與Weex和RN框架使用的JsCore轉化的中間層不一樣,Flutter採用的是全新的架構方案,擁有本身的渲染引擎和Dart上層,每一層都創建在前一層的基礎之上,而且上層比下層的使用頻率更高,其框架架構以下圖所示。
能夠看到,自下而上,Flutter分爲Embedder、Engine和Framework三層。其中,Embedder是操做系統適配層,主要負責Surface渲染設置,線程設置,以及平臺插件等平臺相關特性的適配;Engine層負責圖形繪製、文字排版和提供Dart運行時,Engine層具備獨立虛擬機,正是因爲它的存在,Flutter程序才能運行在不一樣的平臺上,實現跨平臺運行;Framework層則是使用Dart編寫的一套基礎視圖庫,包含了動畫、圖形繪製和手勢識別等功能,是使用頻率最高的一層。架構
不論是什麼渲染框架,其基本的原理都是:通常以60Hz的固定頻率刷新,每一幀圖像繪製完成後,會繼續繪製下一幀,而後顯示器就會發出一個Vsync信號,按60Hz計算,屏幕每秒會發出60次這樣的信號。CPU計算好顯示內容提交給GPU,GPU渲染好交給顯示器顯示。框架
在Flutter中,渲染會用到不少的線程,主要是UI線程和GPU線程,下圖是Flutter App線程的運做原理圖。
下面重點看一下UI線程和GPU線程。異步
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指令,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沒必要要的資源消耗。ui
GPU Runner能夠致使UI Runner的幀調度的延遲,GPU Runner的過載會致使Flutter應用的卡頓,所以在實際使用過程當中,建議爲每個Engine實例都新建一個專用的GPU Runner線程。this
要理解Flutter的渲染原理,那麼就必須瞭解Widget、RenderObject 和 Element及其做用。總的來講,Flutter調用runApp(rootWidget),將rootWidget傳給rootElement,作爲rootElement的子節點,生成Element樹,由Element樹生成Render樹,以下圖所示。
spa
從上面的介紹中,咱們隱約知道了Widget、RenderObject 和 Element的做用,簡單的介紹一下。
Flutter經過Widget樹中的每一個控件建立不一樣類型的渲染對象,組成渲染對象樹,而渲染對象樹在Flutter中的展現分爲四階段:佈局、繪製、合成及渲染。其中,佈局和繪製由RenderObject負責完成,Flutter採用深度優先機制遍歷渲染樹對象,肯定樹中每一個對象的位置和尺寸,並把他們繪製到不一樣的圖層上,而合成及渲染則交給Skia完成。
下圖展現了Widget、Element 和 RenderObject的關係。
在 Flutter 中,萬物皆是 Widget,不管是可見的仍是功能型的,下面是官方對Widget的介紹。
下面是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 方法。
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 完成。
Element 擁有本身的生命週期:
總的來講,Flutter提出一切皆Widget,Widget 主要用來保存 Element 信息,而Element做用Widget 的實例,存放視圖構建的上下文數據,而且同時持有Widget和RenderObject對象,RenderObject的主要做用是處理佈局、繪製相關的事情,肯定Element樹中每一個對象的位置和尺寸。