天天看點

一篇文章了解Flutter架構與渲染

在Flutter誕生之前,已經有許多跨平台UI架構的方案,比如基于WebView的Cordova、AppCan等,還有使用HTML+JavaScript渲染成原生控件的React Native、Weex等。

目前hybrid開發模式:

1.通過WebView來進行原生和web互動

2.為了解決WebView性能差的問題,以React Native為代表的一類架構将最終渲染工作交還給了系統,雖然同樣使用類HTML+JS的UI建構邏輯,但是最終會生成對應的自定義原生控件,以充分利用原生控件相對于WebView的較高的繪制效率。

一、架構架構

反觀Flutter

首先看一下Flutter的架構圖

一篇文章了解Flutter架構與渲染

Flutter的架構主要分成三層:Framework,Engine和Embedder。

1.Framework使用dart實作,包括Material Design風格的Widget,Cupertino(針對iOS)風格的Widgets,文本/圖檔/按鈕等基礎Widgets,渲染,動畫,手勢等。此部分的核心代碼是:flutter倉庫下的flutter package,以及sky_engine倉庫下的io,async,ui(dart:ui庫提供了Flutter架構和引擎之間的接口)等package。

一篇文章了解Flutter架構與渲染

2.Engine使用C++實作,主要包括:Skia,Dart和Text。Skia是開源的二維圖形庫,提供了适用于多種軟硬體平台的通用API。

3.Embedder是一個嵌入層,即把Flutter嵌入到各個平台上去,這裡做的主要工作包括渲染Surface設定,線程設定,以及插件等。從這裡可以看出,Flutter的平台相關層很低,平台(如iOS)隻是提供一個畫布,剩餘的所有渲染相關的邏輯都在Flutter内部,這就使得它具有了很好的跨端一緻性。

從架構圖可以看出,從頭到尾重寫一套跨平台的UI架構,包括UI控件、渲染邏輯甚至開發語言。渲染引擎依靠跨平台的Skia圖形庫來實作,依賴系統的隻有圖形繪制相關的接口,可以在最大程度上保證不同平台、不同裝置的體驗一緻性,邏輯處理使用支援AOT的Dart語言,執行效率也比JavaScript高得多。

二、widget

萬物皆widget

目前上主流的思想,都希望将各個ui控件接耦,慢慢演變出元件化的思想。

Flutter控件主要分為兩大類,StatelessWidget和StatefulWidget,StatelessWidget用來展示靜态的文本或者圖檔,如果控件需要根據外部資料或者使用者操作來改變的話,就需要使用StatefulWidget。State的概念也是來源于Facebook的流行Web架構React,React風格的架構中使用控件樹和各自的狀态來建構界面,當某個控件的狀态發生變化時由架構負責對比前後狀态差異并且采取最小代價來更新渲染結果。

widget效果見這兩篇文章 widget 應用一  widget 應用二

接下來主要看下渲染樹和flutter引擎綁定在一起,看一下binding.dart、object.dart,分析下flutter是如何渲染的

abstract class RendererBinding extends BindingBase with ServicesBinding, SchedulerBinding, HitTestable { ... }
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
abstract class RenderBox extends RenderObject { ... }
class RenderParagraph extends RenderBox { ... }
class RenderImage extends RenderBox { ... }
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
                                        RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData>,
                                        DebugOverflowIndicatorMixin { ... }
           

建立[RenderView]對象作為[RenderObject]呈現樹的根,并對其進行初始化,以便在引擎下次準備顯示一個架構時将其呈現。

建立綁定是自動調用

每個架構由以下幾個階段組成:

1. The animation phase:

 2. Microtasks: 

3. The layout phase: 布局階段:

布局階段:系統中所有髒的[RenderObject]都被放置(見[RenderObject.performLayout])。[RenderObject.markNeedsLayout]

有關為布局标記髒對象的詳細資訊

4. The compositing bits phase: 合成部分階段

5. The paint phase:繪制階段:

系統中所有的渲染對象都是重新繪制(見[RenderObject.paint])。這将生成[層]樹

 6. The compositing phase:

合成階段:将圖層樹轉換為[場景]和發送到GPU。

7. The semantics phase:語義階段

8. The finalization phase:結束階段

看一下代碼當中的具體執行 

@protected
  void drawFrame() {
    assert(renderView != null);
    pipelineOwner.flushLayout(); //1
    pipelineOwner.flushCompositingBits();//2
    pipelineOwner.flushPaint();//3
    renderView.compositeFrame(); // this sends the bits to the GPU
    pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. //4
  }
           

1。更新任何需要計算它們的渲染對象布局。在這個階段,每個渲染的大小和位置計算對象。渲染對象可能會弄髒他們的畫或這一階段的合成狀态。

2。更新有dirty的渲染對象合成部分。在這個階段,每個呈現對象都知道是否它的任何一個孩子都需要合成。此資訊在期間使用

繪畫階段選擇如何實作視覺效果等剪裁。如果呈現對象有一個合成的子對象,它需要使用一個

[層]來建立剪輯,以便該剪輯應用于合成子元素(将被繪制到它自己的[圖層]中)。

3.通路任何需要繪制的渲染對象。在這階段,渲染對象有機會記錄繪制指令[圖檔圖層],并建構其他合成的[圖層]。

4.将編譯呈現對象的語義。這個語義資訊被使用輔助技術,以改善渲染樹的可通路性。

一篇文章了解Flutter架構與渲染
一篇文章了解Flutter架構與渲染

渲染對象樹中的每個對象都會在布局過程中接受父對象的

Constraints

參數,決定自己的大小,然後父對象就可以按照自己的邏輯決定各個子對象的位置,完成布局過程。子對象不存儲自己在容器中的位置,是以在它的位置發生改變時并不需要重新布局或者繪制。子對象的位置資訊存儲在它自己的

parentData

字段中,但是該字段由它的父對象負責維護,自身并不關心該字段的内容。同時也因為這種簡單的布局邏輯,Flutter可以在某些節點設定布局邊界(Relayout boundary),即當邊界内的任何對象發生重新布局時,不會影響邊界外的對象,反之亦然:

一篇文章了解Flutter架構與渲染

布局完成後,渲染對象樹中的每個節點都有了明确的尺寸和位置,Flutter會把所有對象繪制到不同的圖層上:

一篇文章了解Flutter架構與渲染

因為繪制節點時也是深度周遊,可以看到第二個節點在繪制它的背景和前景不得不繪制在不同的圖層上,因為第四個節點切換了圖層(因為“4”節點是一個需要獨占一個圖層的内容,比如視訊),而第六個節點也一起繪制到了紅色圖層。這樣會導緻第二個節點的前景(也就是“5”)部分需要重繪時,和它在邏輯上毫不相幹但是處于同一圖層的第六個節點也必須重繪。為了避免這種情況,Flutter提供了另外一個“重繪邊界”的概念:

在進入和走出重繪邊界時,Flutter會強制切換新的圖層,這樣就可以避免邊界内外的互相影響。典型的應用場景就是ScrollView,當滾動内容重繪時,一般情況下其他内容是不需要重繪的。雖然重繪邊界可以在任何節點手動設定,但是一般不需要我們來實作,Flutter提供的控件預設會在需要設定的地方自動設定

總結

在Flutter界面渲染過程分為三個階段:布局、繪制、合成,布局和繪制在Flutter架構中完成,合成則交由引擎負責。

一篇文章了解Flutter架構與渲染

Flutter vs ReactNative架構對比

RN的效率由于是将View編譯成了原生View,是以效率上要比基于Cordova的HTML5高很多,但是它也有效率問題,RN的渲染機制是基于前端架構的考慮,複雜的UI渲染是需要依賴多個view疊加.比如我們渲染一個複雜的ListView,每一個小的控件,都是一個native的view,然後互相組合疊加.想想此時如果我們的list再需要滑動重新整理,會有多少個對象需要渲染.是以也就有了前面所說的RN的清單方案不友好;

Flutter 吸收了前兩者的教訓之後,在渲染技術上,選擇了自己實作(GDI),由于有更好的可控性,使用了新的語言Dart,避免了RN的那種通過橋接器與Javascript通訊導緻效率低下的問題,是以在性能方面比RN更高一籌;

一篇文章了解Flutter架構與渲染
一篇文章了解Flutter架構與渲染
一篇文章了解Flutter架構與渲染

ReactNative

  • 采用Javascript開發,需學React,成本高
  • 需要JavaScript橋接器,實作JS到Native轉化,性能耗損
  • 通路原生UI,頻繁操作易出性能問題 
  • 支援線上動态性,可有效避免頻繁更新版本

Flutter

  • 采用Dart開發,可直接編譯成Native代碼(易學)
  • 自帶UI元件和渲染器,僅依賴系統提供的Canvas(無橋接耗損)
  • 暫不支援線上動态性,目前Android支援,ios不支援

參考 Flutter原理和美團的實踐

       https://blog.csdn.net/zhangzeshuai/article/details/80151710