👏歡迎前往本人的GitHub查看更多內容。點擊前往GitHubhtml
Debug Debug模式能夠在真機和模擬器上同時運行:會打開全部的斷言,包括debugging信息、debugger aids(好比observatory)和服務擴展。優化了快速develop/run循環,可是沒有優化執行速度、二進制大小和部署。命令flutter run就是以這種模式運行的,經過sky/tools/gn --android或者sky/tools/gn --ios來build。有時候也被叫作「checked模式」或者「slow模式」。 Release Release模式只能在真機上運行,不能在模擬器上運行:會關閉全部斷言和debugging信息,關閉全部debugger工具。優化了快速啓動、快速執行和減少包體積。禁用全部的debugging aids和服務擴展。這個模式是爲了部署給最終的用戶使用。命令flutter run --release就是以這種模式運行的,經過sky/tools/gn --android --runtime-mode=release或者sky/tools/gn --ios --runtime-mode=release來build。 Profile Profile模式只能在真機上運行,不能在模擬器上運行:基本和Release模式一致,除了啓用了服務擴展和tracing,以及一些爲了最低限度支持tracing運行的東西(好比能夠鏈接observatory到進程)。命令flutter run --profile就是以這種模式運行的,經過sky/tools/gn --android --runtime-mode=profile或者sky/tools/gn --ios --runtime-mode=profile```來build。由於模擬器不能表明真實場景,因此不能在模擬器上運行。 test headless test模式只能在桌面上運行:基本和Debug模式一致,除了是headless的並且你能在桌面運行。命令flutter test就是以這種模式運行的,經過sky/tools/gn來build。 複製代碼
1.Framework使用dart實現,包括Material
Design風格的Widget,Cupertino(針對iOS)風格的Widgets,文本/圖片/按鈕等基礎Widgets,渲染,動畫,手勢等。
此部分的核心代碼是:flutter倉庫下的flutter
package,以及sky_engine倉庫下的io,async,ui(dart:ui庫提供了Flutter框架和引擎之間的接口)等package。
2.Engine使用C++實現,主要包括:Skia,Dart和Text。
Skia是開源的二維圖形庫,提供了適用於多種軟硬件平臺的通用API。
3.Embedder是一個嵌入層,即把Flutter嵌入到各個平臺上去,這裏作的主要工做包括渲染Surface設置,線程設置,以及插件等。
從這裏能夠看出,Flutter的平臺相關層很低,平臺(如iOS)只是提供一個畫布,剩餘的全部渲染相關的邏輯都在Flutter內部,這就使得它具備了很好的跨端一致性。
複製代碼
Widget實際上就是Element的配置數據,Widget樹其實是一個配置樹,而真正的UI渲染樹是由Element構成;不過,因爲Element是經過Widget生成,因此它們之間有對應關係,因此在大多數場景,咱們能夠寬泛地認爲Widget樹就是指UI控件樹或UI渲染樹。
一個Widget對象能夠對應多個Element對象。這很好理解,根據同一份配置(Widget),能夠建立多個實例(Element)。
從建立到渲染的大致流程是:根據Widget生成Element,而後建立相應的RenderObject並關聯到Element.renderObject屬性上,最後再經過RenderObject來完成佈局排列和繪製。
複製代碼
答案:能夠,可是調試很不許確。因此不建議使用模擬器進行性能調試。
幾乎所有的 Flutter 應用性能調試都應該在真實的 Android 或者 iOS 設備上以分析模式進行。
一般來講,調試模式或者是模擬器上運行的應用的性能指標和發佈模式的表現並不相同。
應該考慮在用戶使用的最慢的設備上檢查性能。
複製代碼
爲何應該在真機上運行:android
各類模擬器使用的硬件並不相同,所以性能也不一樣—模擬器上的一些操做會比真機快,而另外一些操做則會比真機慢。ios
調試模式相比分析模式或者發佈編譯來講,增長了額外的檢查(例如斷言),這些檢查可能至關耗費資源。git
調試模式和發佈模式代碼執行的方式也是不一樣的。調試編譯採用的是「just in time」(JIT)模式運行應用,而分析和發佈模式則是預編譯到本地指令(「ahead of time」,或者叫 AOT)以後再加載到設備中。JIT自己的編譯就可能致使應用暫停,從而致使卡頓。github
答案: 1.在 Android Studio 和 IntelliJ 使用 Run > Flutter Run main.dart in Profile Mode 選項 1.1 選擇 View > Tool Windows > Flutter Inspector。 1.2 在工具欄中選擇書架圖標。 2.在 VS Code中,打開 launch.json 文件,設置 flutterMode 屬性爲 profile(當分析完成後,改回 release 或者 debug) 2.1 選擇 View > Command Palette… 來打開 command palette。 2.2 在文本框中輸入「performance」並在彈出列表中選中 Toggle Performance Overlay。若是命令不可用,請確保應用在運行狀態。 3.From the command line, use the --profile flag: 命令行使用 --profile 參數運行 3.1 flutter run --profile 3.2 使用 p 參數觸發性能圖層 4.能夠經過在 MaterialApp 或者 WidgetsApp 的構造方法中設置 showPerformanceOverlay 屬性爲 true 來展現 PerformanceOverlay widget: class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( showPerformanceOverlay: true, title: 'My Awesome App', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'My Awesome App'), ); } } 複製代碼
答案:
性能圖層用兩張圖表顯示應用的耗時信息。若是 UI 產生了卡頓(跳幀),這些圖表能夠幫助分析緣由。圖表在當前應用的最上層展現,但並非用普通的 widget 方式繪製的—Flutter 引擎自身繪製了該圖層來儘量減小對性能的影響。每一張圖表都表明當前線程的最近 300 幀表現。
左圖:GPU 線程的性能狀況在上面,UI 線程顯示在下面,垂直的綠色條條表明的是當前幀。
右圖:每一幀渲染過程中總共使用的時間
複製代碼
Flutter 用了一些額外的線程來完成這項工做。開發者的 Dart 代碼都在 UI 線程運行。儘管沒有直接訪問其餘線程的權限,但 UI 線程的動做仍是對其餘線程的性能有影響的。
平臺線程
該平臺的主線程。插件代碼在這裏運行。更多信息請參閱:iOS 的 UIKit 文檔,或者 Android 的 MainThread 文檔。性能圖層並不會展現該線程。
UI 線程
UI 線程在 Dart VM 執行 Dart 代碼。該線程包括開發者寫下的代碼和 Flutter 框架根據應用行爲生成的代碼。當應用建立和展現場景的時候,UI 線程首先創建一個 圖層樹(layer tree) ,一個包含設備無關的渲染命令的輕量對象,並將圖層樹發送到 GPU 線程來渲染到設備上。 不要阻塞這個線程! 在性能圖層的最低欄展現該線程。
GPU 線程
GPU 線程取回圖層樹並通知 GPU 渲染。儘管沒法直接與 GPU 線程或其數據通訊,但若是該線程變慢,必定是開發者 Dart 代碼中的某處致使的。圖形庫 Skia 在該線程運行,有時也被叫作 光柵器(rasterizer)線程 。在性能圖層的最頂欄展現該線程。
I/O 線程
可能阻塞 UI 或者 GPU 線程的耗時任務(大多數狀況下是I/O)。該線程並不會在性能圖層中展現。
複製代碼
紅色豎條代表當前幀的渲染和繪製都很耗時 當兩張圖表都是紅色時,就要開始對 UI 線程(Dart VM)進行診斷了。
每一幀都應該在 1/60 秒(大約 16ms)內建立並顯示。
若是有一幀超時(任意圖像)而沒法顯示,就致使了卡頓,圖表之一就會展現出來一個紅色豎條。
若是是在 UI 圖表出現了紅色豎條,則代表 Dart 代碼消耗了大量資源。
而若是紅色豎條是在 GPU 圖表出現的,意味着場景太複雜致使沒法快速渲染。
複製代碼
爲何須要在 16ms 內渲染完成每一幀
1.將幀渲染時間下降到 16ms 如下可能在視覺上看不出來什麼變化,但能夠延長電池壽命以及避免發熱問題。
2.可能在你當前測試設備上運行良好,但請考慮在應用所支持的最低端設備上的狀況。
3.當 120fps 的設備普及以後,便須要在 8ms 以內完成每一幀的渲染來保證流暢平滑的體驗。
複製代碼
若是性能圖層的 UI 圖表顯示紅色,就要從分析 Dart VM 開始着手了,即便 GPU 圖表一樣顯示紅色。
使用 Dart DevTool 進行性能分析
Dart DevTool 提供諸如性能分析、堆測試以及顯示代碼覆蓋率等功能。
DevTool 的 timeline 界面可讓開發者逐幀分析應用的 UI 性能。
複製代碼
(Observatory 被 Dart DevTools 取代了。這個基於瀏覽器的工具仍在開發中,但只用來預覽。參考 DevTools’ docs 頁面來獲取安裝和使用指導。)chrome
有些狀況下界面的圖層樹構造起來雖然容易,但在 GPU 線程下渲染卻很耗時。
這種狀況發生時,UI 圖表沒有紅色,但 GPU 圖表會顯示紅色。
這時須要找出代碼中致使渲染緩慢的緣由。
特定類型的負載對 GPU 來講會更加複雜。
可能包括沒必要要的對 saveLayer 的調用,許多對象間的複雜操做,還多是特定情形下的裁剪或者陰影。
複製代碼
若是推斷的緣由是動畫中的卡頓的話,可使用 timeDilation 屬性來極大地放慢動畫。也可使用 Flutter Inspector 來減慢動畫速度。在 inspector 的 gear 菜單下選中 Enable Slow Animations。若是想對動畫速度進行更多操做,請在代碼中設置 timeDilation 屬性。卡頓是第一幀發生的仍是貫穿整個動畫過程呢?若是是整個動畫過程的話,會是裁剪致使的麼?也許有能夠替代裁剪的方法來繪製場景。好比說,不透明圖層的長方形中用尖角來取代圓角裁剪。若是是一個靜態場景的淡入、旋轉或者其餘操做,能夠嘗試使用 RepaintBoundary。
json
saveLayer 方法是 Flutter 框架中最重量的操做之一。 更新屏幕時這個方法頗有用,但它可能使應用變慢,若是不是必須的話,應該避免使用這個方法。 即使沒有顯式地調用 saveLayer,也可能在其餘操做中間接調用了該方法。可使用 PerformanceOverlayLayer.checkerboardOffscreenLayers 開關來檢查場景是否使用了 saveLayer。 打開開關以後,運行應用並檢查是否有圖像的輪廓閃爍。若是有新的幀渲染的話,容器就會閃爍。 舉個例子,也許有一組對象的透明度要使用 saveLayer 來渲染。 在這種狀況下,相比經過 widget 樹中高層次的父 widget 操做,單獨對每一個 widget 來應用透明度可能性能會更好。其餘可能大量消耗資源的操做也同理,好比裁剪或者陰影。
api
透明度(Opacity)、裁剪(clipping)以及陰影(shadows)它們自己並非個糟糕的注意。然而對 widget 樹頂層 widget 的操做可能致使額外對 saveLayer 的調用以及無用的處理。
android-studio
RepaintBoundary 使用 RepaintBoundary 來緩存圖片是個好主意, 當須要的時候 。 從資源的角度看,最重量級的操做之一是用圖像文件來渲染紋理。 首先,須要從持久存儲中取出壓縮圖像,而後解壓縮到宿主存儲中(GPU 存儲),再傳輸到設備存儲器中(RAM)。也就是說,圖像的 I/O 操做是重量級的。 緩存提供了複雜層次的快照,這樣就能夠方便地渲染到隨後的幀中。 由於光柵緩存入口的構建須要大量資源,同時增長了 GPU 存儲的負載,因此只在必須時才緩存圖片。 打開PerformanceOverlayLayer.checkerboardRasterCacheImages 開關能夠檢查哪些圖片被緩存了。 運行應用來查看使用隨機顏色網格渲染的圖像,標識被緩存的圖像。當和場景交互時,網格里的圖片應該是靜止的—表明從新緩存圖片的閃爍視圖不該該出現。 大多數狀況下,開發者都但願在網格里看到的是靜態圖片,而不是非靜態圖片。若是靜態圖片沒有被緩存,能夠將其放到 RepaintBoundary widget 中來緩存。雖然引擎也可能忽略 repaint boundary,若是它認爲圖像還不夠複雜的話。
顯示性能數據 Flutter 框架的設計使得構建達不到 60fps 流暢度的應用變得困難。一般狀況下若是卡頓,就是由於每一幀被重建的 UI 比需求更多的簡單 bug。Widget rebuild profiler 能夠幫助調試和修復這些問題引發的 bug。 能夠檢視 widget inspector 中當前屏幕和幀下的 widget 重建數量。瞭解細節,能夠參考 在 Android Studio 或類 IntelliJ 裏開發 Flutter 應用 中的 顯示性能數據。
1.在mian裏面設置
2.profile下真機運行
3.選擇Open TimeLine View,建議使用chrome打開
4.查看分析
1.避免在 build() 方法中進行重複且耗時的工做,由於當父 Widget 重建時,子 Wdiget 的 build() 方法會被頻繁地調用。
2.當在 State 上調用 setState()時,全部後代 Widget 都將重建。所以,將 setState() 的調用轉移到其 UI 實際須要更改的 Widget 子樹部分。若是改變的部分僅包含在 Widget 樹的一小部分中,請避免在 Widget 樹的更高層級中調用 setState()。【提升build的效率- 下降遍歷的出發點】
3.當從新遇到與前一幀相同的子 Widget 實例時,將中止遍歷。這種技術在框架內部大量使用,用於優化動畫不影響子樹的動畫。請參閱 TransitionBuilder 模式和使用此原則的 SlideTransition,以免在動畫過程當中重建其後代 Widget。【提升build的效率- 中止樹的遍歷】
4.須要更新的地方添加RepaintBoundary去設置一個獨立圖層,來減小圖層更新節點的數量【提升paint的效率】
由於Dart代碼直接調用SKia的C和C++代碼,當Dart代碼可以媲美Java代碼就可以達到Flutter App的性能媲美原生App。
Skia(開源圖形引擎)是一個C++的開源2D向量圖形處理函數庫(Cairo是一個矢量庫),包括字型、座標轉換、位圖等等,至關於輕量級的Cairo,目前主要用於Google的Android和Chrome平臺,Skia搭配OpenGL/ES與特定的硬件特徵,強化顯示的效果。另外,Skia是WebKit支持的衆多圖形平臺之一,在WebKit的GraphicsContext.h/.c中有相關實現。
使用真機進行性能調試,Skia 有兩套很不一樣的後端,Flutter在iOS模擬器中使用純CPU後端,而真機設備通常使用GPU硬件加速後端,因此性能特性很不同
1.在項目路徑下運行:flutter run --profile --trace-skia
2.點擊運行完成後的連接,打開的其實就是TimeLine View,但這時候須要選擇All,把全部函數都勾選上
3.而後操做App,點擊refresh生成渲染圖表。
4.flutter 將一幀錄製成SkPicture(skp)送給Skia進行渲染。
用flutter screenshot --type=skia --observatory-port=<port>捕捉skp,並利用[debugger.skia.org]()咱們能夠上傳skp而後單步分析每一條繪圖指令。
複製代碼
1.避免使用 Opacity widget,尤爲是在動畫中避免使用。請用 AnimatedOpacity 或 FadeInImage 進行代替。更多信息,請參閱:Performance considerations for opacity animation
有關將透明度直接應用於圖像的示例,請參見 Transparent image,這比使用 Opacity widget 更快。
For example:
Container(color: Color.fromRGBO(255, 0, 0, 0.5)) 👍
Opacity(opacity: 0.5, child: Container(color: Colors.red)). 🙅
複製代碼
2.Clip 不會調用 saveLayer()(除非明確使用 Clip.antiAliasWithSaveLayer),所以這些操做沒有 Opacity 那麼耗時,但仍然很耗時,因此請謹慎使用。
3.若是大多數 children widget 在屏幕上不可見,請避免使用返回具體列表的構造函數(例如 Column() 或 ListView()),以免構建成本。使用帶有回調的惰性方法(例如ListView.builder)。
4.避免調用 saveLayer()。
【爲何 saveLayer 代價很大?】 調用 saveLayer() 會開闢一片離屏緩衝區。將內容繪製到離屏緩衝區可能會觸發渲染目標切換,這些切換在較早期的 GPU 中特別慢。 下面可能觸發saveLayer 1 ShaderMask 2 ColorFilter 3 Chip -- might cause call to saveLayer() if disabledColorAlpha != 0xff 4 Text -- might cause call to saveLayer() if there’s an overflowShader 避免調用 saveLayer() 的方式: 1: 要在圖像中實現淡入淡出,請考慮使用 FadeInImage 小部件,該小部件使用 GPU 的片斷着色器應用漸變不透明度。瞭解更多詳情,請參見 Opacity 文檔。 2: 要建立帶圓角的矩形,而不是應用剪切矩形,請考慮使用不少 widget 都提供的 borderRadius屬性。 複製代碼
5.當有些widget被遮擋住了,不須要渲染了,可使用Visibility來控制不可見。
6.使用 AnimatedBuilder 時,請避免在不依賴於動畫的 widget 的構造方法中構建 widget 樹。動畫的每次變更都會重建這個 widget 樹。而應該構建子樹的那一部分,並將其做爲 child 傳遞給 AnimatedBuilder。
7.避免在動畫中剪裁。若是可能,請在動畫開始以前預先剪切圖像。
8.優化頁面當有大量圖片加載的時候,性能的消耗,好比下降圖片質量來下降
參考:
👇推薦👇: