Flutter 高性能原理淺析

這是我第三篇Flutter相關博客 歡迎 查看個人前兩篇 Flutter實現篇html

前言

Flutter是Google用以幫助開發者在Ios和Android兩個平臺開發高質量原生應用的全新移動UI框架.我開始認識Flutter時,經歷了三個Flutter重要歷史版本.前端

  • 2018年2月27日,在2018世界移動大會上,Google發佈了Flutter的第一個Beta版本。
  • 2018年6月21日,在全球大前端技術大會上,發佈了第一個Release Perview 1版本。
  • 2018年12月5日第一個Release 1.0版本發佈.

此後愈來愈多的人開始關注到Flutter。web

在 Flutter 誕生以前,已經有許多跨平臺 UI 框架的方案,好比基於 WebView 的 Cordova、AppCan 等,還有使用 HTML+JavaScript 渲染成原生控件的 React Native、Weex 等。Flutter 則開闢了一種全新的思路,從頭到 尾重寫一套跨平臺的 UI 框架,包括 UI 控件、渲染邏輯甚至開發語言。算法

Flutter框架

從圖中能夠看出 Flutter主要被分爲兩層 Framework層和Flutter Engine.網絡

Framework層所有使用Dart編寫,有完整UI框架的API,並預寫了Android(MaterialDesign)和IOS的(Cupertino)風格的UI,極大方便了開發移動端.數據結構

Framework 底層是 Flutter 引擎, 引擎主要負責圖形繪製 (Skia)、 文字排版 (libtxt) 和提供 Dart 運行時, 引擎所有使用 C++實現.多線程

Flutter高性能原理

與其餘跨平臺框架對比

在看Flutter框架前,咱們先看一下其餘跨平臺框架的設計架構

看Hybrid的架構,咱們能夠知道UI層的渲染是基於Webview去渲染,他的性能取決於webview的渲染性能,目前已知webview渲染性能 < NativeUI的性能

RN/Weex 的架構中,是基於Native的UI框架去適配,中間多了一層js轉NativeUI的過程

而Flutter不須要中間層(Webview,js 轉NativeUI這個過程),他是基於圖像渲染引擎去直接繪製UI.

Dart 對於UI框架的高性能支持

咱們知道Flutter的Framework層是使用了Dart語言編寫,那Dart語言有哪些優點呢?下面分爲幾個點來闡述框架

Dart內存分配機制

DartVM的內存分配策略很是簡單,建立對象時只須要在現有堆上移動指針,內存增加始終是線形的,省去了查找可用內存段的過程異步

Dart中相似線程的概念叫作Isolate,每一個Isolate之間是沒法共享內存的,因此這種分配策略可讓Dart實現無鎖的快速分配。

Dart 垃圾回收機制

Dart的垃圾回收也採用了多生代算法,新生代在回收內存時採用了「半空間」算法,觸發垃圾回收時Dart會將當前半空間中的「活躍」對象拷貝到備用空間,而後總體釋放當前空間的全部內存如圖.

整個過程當中Dart只須要操做少許的「活躍」對象,大量的沒有引用的「死亡」對象則被忽略,這種 多生代無鎖垃圾回收器,專門爲UI框架中常見的大量Widgets對象建立和銷燬優化,很是適合Flutter框架中大量Widget重建的場景.

Dart 編體積優化,及編譯JIT和AOT支持

代碼體積優化(Tree Shaking),編譯時只保留運行時須要調用的代碼(不容許反射這樣的隱式引用),因此龐大的Widgets庫不會形成發佈體積過大。

Dart支持兩種編譯模式:

  • JIT編譯 Just In Time Compiler -即時編譯
  • AOT編譯Ahead Of Time 預編譯

在debug模式下使用JIT編譯,生成srcipt/bytecode進行解釋執行,能夠支持HotReload(熱重載),修改代碼,保持便可在設備上看到效果. 而在Release下 AOT編譯生成Machine Code,高效的運行.

Dart 單線程 異步消息機制

客戶端交互簡述

對於移動端的交互來講,大多數狀況下都是在等待狀態,等待網絡請求,等待用戶輸入等.那麼設想一下,發起一個網絡請求只在一個線程中能夠進行嗎?固然網絡請求確定是異步的(注意這裏說的異步而多線程並不是一個概念.),事實驗證是能夠的,Flutter就採用了Dart這種單線程機制,省去了多線程上下文切換帶來的性能損耗.(對於高耗時操做,也一樣支持多線程操做,經過Isolate開啓,不過注意這裏的多線程,內存是沒法共享的.)

Dart 異步消息原理

當一個Dart的方法開始執行時,他會一直執行直至達到這個方法的退出點。換句話說Dart的方法是不會被其餘Dart代碼打斷的。 當一個Dart應用開始的標誌是它的main isolate執行了main方法。當main方法退出後,main isolate的線程就會去逐一處理消息隊列中的消息。

有了消息隊列,而後有了循環去讀取消息隊列中的消息,就能夠有單線程去執行異步消息的能力. 通常的消息使用dart:async中使用Future來支持異步消息.

Flutter Engine 高性能

在講Flutter Engin層時,咱們先講一下屏幕繪製的原理.

屏幕繪製原理

咱們都知道顯示器以固定的頻率刷新,好比 iPhone的 60Hz、iPad Pro的 120Hz。當一幀圖像繪製完畢後準備繪製下一幀時,顯示器會發出一個垂直同步信號(VSync),因此 60Hz的屏幕就會一秒內發出 60次這樣的信號。

而且通常地來講,計算機系統中,CPU、GPU和顯示器以一種特定的方式協做:CPU將計算好的顯示內容提交給 GPU,GPU渲染後放入幀緩衝區,而後視頻控制器按照 VSync信號從幀緩衝區取幀數據傳遞給顯示器顯示。

做爲一個專職Android開發,看過Android的繪圖機制,經過SurfaceFlinger 和HAL層之間的工做機制發現和Flutter的很像,那麼IOS的如何呢?我的推測屏幕的繪圖機制是同樣的,只是不一樣平臺有不一樣實現.

Flutter Engine的渲染機制

Flutter只關心向 GPU提供視圖數據,GPU的 VSync信號同步到 UI線程,UI線程使用 Dart來構建抽象的視圖結構,這份數據結構在 GPU線程進行圖層合成,視圖數據提供給 Skia引擎渲染爲 GPU數據,這些數據經過 OpenGL或者 Vulkan提供給 GPU.

因此 Flutter並不關心顯示器、視頻控制器以及 GPU具體工做,它只關心 GPU發出的 VSync信號,儘量快地在兩個 VSync信號之間計算併合成視圖數據,而且把數據提供給 GPU.

Flutter Framework層的繪圖機制

UI樹原理

在 Flutter 界面渲染過程分爲 3 個階段: 佈局、繪製、合成.

而佈局階段,有三個重要的對象.RenderObject、Element、Widget.

  • Widget是開發常常接觸的控件,默認是隻讀的.

  • Element 是 Flutter 用來分離控件樹和真正的渲染 對象的中間層, 控件用來描述對應的 element 屬性,控件重建後可能會複用同一個 element.

  • RenderObject 負責提供配置信息並建立具體的 Element。

Element 持有真正負責佈局、 繪製和碰撞測試 (hit test) 的 RenderObject 對象.

那麼這樣,若是控件的屬性發生了變化 (由於控件的屬性是隻 讀的, 因此變化也就意味着從新建立了新的控件樹), 可是其樹上每一個節點的類型沒有變化時, element 樹和 render 樹能夠徹底重用原來的對象 (由於 element 和 render object 的屬性都是可變的)

佈局原理

傳統佈局,如Android可能須要屢次Measure,計算寬高。Flutter 採用約束進行單次測量佈局. 整個佈局過程當中只須要深度遍歷一次,極大的提高效能。

渲染對象樹中的每一個對象都會在佈局過程當中接受父 對象的 Constraints 參數,決定本身的大小, 而後父對象 就能夠按照本身的邏輯決定各個子對象的位置,完成佈局過程.

子對象不存儲本身在容器中的位置, 因此在它的位置發生改變時並不須要從新佈局或者繪製. 子對象的位 置信息存儲在它本身的 parentData 字段中,可是該字段由它的父對象負責維護,自身並不關心該字段的內容。

同時也由於這種簡單的佈局邏輯, Flutter 能夠在某些節 點設置佈局邊界 (Relayout boundary), 即當邊界內的任 何對象發生從新佈局時, 不會影響邊界外的對象, 反之亦然.

參考資料

文中參考的資料以下

相關文章
相關標籤/搜索