概述

#

應用效能包含多個方面,從原始速度和 I/O 吞吐量到使用者介面的流暢度。雖然本頁主要關注 UI 流暢度(無卡頓或掉幀),但這裡介紹的工具通常也可用於診斷其他效能問題。

Flutter 提供了幾種效能分析工具。以下是其中一些:

  • 效能疊加層:直接在執行的應用中顯示一套簡化的指標。要了解更多資訊,請參閱本主題中的相關章節。

  • 效能檢視:一個基於 Web 的介面,可連線到您的應用並顯示詳細的效能指標。這是 DevTools 工具的一部分。要了解更多資訊,請參閱使用效能檢視

  • Dart 中的效能跟蹤:使用 dart:developer 包直接在應用的 Dart 程式碼中新增跟蹤,然後在 DevTools 工具中跟蹤應用的效能。要了解更多資訊,請參閱跟蹤 Dart 程式碼

  • 基準測試:您可以透過編寫基準測試來衡量和跟蹤應用的效能。Flutter Driver 庫提供了對基準測試的支援。使用此整合測試框架,您可以生成跟蹤掉幀、下載大小、電池效率和啟動時間的指標。有關更多資訊,請檢視整合測試

  • Widget 重建剖析器 (IntelliJ/Android Studio):掉幀通常源於不必要的 UI 重建。如果您使用的是 IntelliJ/Android Studio,Widget 重建剖析器透過顯示當前螢幕和幀的 Widget 重建計數來幫助定位和修復這些問題。有關更多資訊,請參閱顯示效能資料

Flutter 的目標是提供每秒 60 幀 (fps) 的效能,或在支援的裝置上提供 120 fps。為了達到 60 fps,每幀必須在大約 16 毫秒內渲染完成,以避免掉幀。當幀渲染時間顯著增加並被丟棄時,就會發生掉幀,導致動畫出現明顯的卡頓。例如,如果一幀的渲染時間偶爾是平常的 10 倍,那麼它很可能會被丟棄,導致動畫看起來卡頓。

連線到物理裝置

#

幾乎所有 Flutter 應用的效能除錯都應在物理 Android 或 iOS 裝置上進行,並且您的 Flutter 應用應以profile 模式執行。使用 debug 模式或在模擬器/模擬器上執行應用,通常不能反映 release 模式構建的最終行為。您應該考慮在使用者可能合理使用的最慢的裝置上檢查效能。

以 profile 模式執行

#

Flutter 的 profile 模式以幾乎與 release 模式相同的方式編譯和啟動您的應用程式,但增加了足夠多的額外功能以允許除錯效能問題。例如,profile 模式會向性能分析工具提供跟蹤資訊。

按以下方式以 profile 模式啟動應用

  • 在 VS Code 中,開啟您的 launch.json 檔案,並將 flutterMode 屬性設定為 profile(完成效能分析後,將其改回 releasedebug)。

    json
    "configurations": [
      {
        "name": "Flutter",
        "request": "launch",
        "type": "dart",
        "flutterMode": "profile"
      }
    ]
  • 在 Android Studio 和 IntelliJ 中,使用 **Run > Flutter Run main.dart in Profile Mode** 選單項。

  • 從命令列,使用 --profile 標誌。

    flutter run --profile

有關不同模式的更多資訊,請參閱Flutter 的構建模式

您將首先開啟 DevTools 並檢視效能疊加層,如下一節所述。

啟動 DevTools

#

DevTools 提供效能分析、檢查堆、顯示程式碼覆蓋率、啟用效能疊加層和逐步偵錯程式等功能。DevTools 的時間線檢視允許您逐幀檢視應用的 UI 效能。

一旦您的應用以 profile 模式執行,就啟動 DevTools

顯示效能疊加層

#

您可以按以下方式切換效能疊加層的顯示

  • DevTools 效能檢視:從 DevTools 中的 效能檢視啟用 PerformanceOverlay widget 的最簡單方法。只需點選 **Performance Overlay** 按鈕即可在執行的應用上切換疊加層。

  • 命令列:從命令列使用 **P** 鍵切換效能疊加層。

  • 以程式設計方式:要以程式設計方式啟用疊加層,請參閱效能疊加層,這是以程式設計方式除錯 Flutter 應用頁面中的一個部分。

觀察效能疊加層

#

效能疊加層在兩個圖表中顯示統計資訊,這些圖表顯示了您的應用中花費時間的位置。如果 UI 出現卡頓(丟幀),這些圖表可以幫助您找出原因。圖表顯示在執行的應用之上,但它們的繪製方式不同於普通 widget——Flutter 引擎本身繪製疊加層,並且對效能的影響最小。每個圖表代表該執行緒的最後 300 幀。

本節介紹如何啟用效能疊加層並使用它來診斷應用中掉幀的原因。下圖顯示了在 Flutter Gallery 示例上執行的效能疊加層。

Screenshot of overlay showing zero jank
效能疊加層顯示光柵執行緒(頂部)和 UI 執行緒(底部)。
垂直綠條代表當前幀。

檢查圖表

#

頂部圖表(標記為“GPU”)顯示光柵執行緒花費的時間,底部圖表顯示 UI 執行緒花費的時間。圖表上的水平白線表示垂直軸上的 16 毫秒增量;如果圖表超出其中一條線,則表示執行頻率低於 60Hz。水平軸表示幀。該圖表僅在您的應用程式繪製時更新,因此如果它處於空閒狀態,圖表就會停止移動。

疊加層應始終在 profile 模式下檢視,因為 debug 模式的效能是為了換取昂貴的斷言而故意犧牲的,這些斷言旨在輔助開發,因此結果具有誤導性。

每一幀都應在 1/60 秒(約 16 毫秒)內建立並顯示。超出此限制的幀(任一圖表)將無法顯示,導致掉幀,並且會在一個或兩個圖表中出現垂直紅條。如果 UI 圖表中出現紅條,則表示 Dart 程式碼過於耗費資源。如果 GPU 圖表中出現垂直紅條,則表示場景過於複雜,無法快速渲染。

Screenshot of performance overlay showing jank with red bars
垂直紅條表示當前幀渲染和繪製都很耗時。
當兩個圖表都顯示紅色時,請先診斷 UI 執行緒。

檢查執行緒

#

Flutter 使用多個執行緒來完成工作,儘管疊加層中只顯示了兩個執行緒。您所有的 Dart 程式碼都在 UI 執行緒上執行。儘管您無法直接訪問任何其他執行緒,但您在 UI 執行緒上的操作會對其他執行緒產生效能影響。

平臺執行緒
平臺的主執行緒。外掛程式碼在此處執行。有關更多資訊,請參閱 iOS 的UIKit文件,或 Android 的MainThread文件。此執行緒未在效能疊加層中顯示。
UI 執行緒
UI 執行緒在 Dart VM 中執行 Dart 程式碼。此執行緒包括您編寫的程式碼以及 Flutter 框架代表您的應用程式執行的程式碼。當您的應用建立並顯示一個場景時,UI 執行緒會建立一個“層樹”(layer tree),這是一個輕量級的物件,包含與裝置無關的繪製命令,然後將層樹傳送到光柵執行緒以在裝置上渲染。不要阻塞此執行緒!顯示在效能疊加層的底部行。
光柵執行緒
光柵執行緒負責處理層樹並透過與 GPU(圖形處理單元)互動來顯示它。您無法直接訪問光柵執行緒或其資料,但如果此執行緒變慢,則是由您在 Dart 程式碼中所做的事情引起的。Skia 和 Impeller(圖形庫)在此執行緒上執行。顯示在效能疊加層的頂部行。請注意,儘管光柵執行緒為 GPU 進行光柵化,但執行緒本身在 CPU 上執行。
I/O 執行緒
執行耗時任務(主要是 I/O),否則這些任務會阻塞 UI 或光柵執行緒。此執行緒未在效能疊加層中顯示。

有關更多資訊和影片的連結,請參閱Flutter Wiki中的框架架構,以及社群文章The Layer Cake

識別問題

#

檢查 UI 圖表

#

如果效能疊加層在 UI 圖表中顯示紅色,請先分析 Dart VM,即使 GPU 圖表也顯示紅色。

檢查 GPU 圖表

#

有時一個場景會生成一個易於構建但光柵執行緒渲染起來很耗時的層樹。在這種情況下,UI 圖表沒有紅色,但 GPU 圖表顯示紅色。在這種情況下,您需要找出您的程式碼在做什麼導致渲染程式碼變慢。特定型別的工作負載對 GPU 來說更具挑戰性。它們可能涉及不必要的對 saveLayer 的呼叫、與多個物件的交叉透明度以及在特定情況下的剪裁或陰影。

如果您懷疑緩慢的來源在動畫期間,請點選 Flutter Inspector 中的 **Slow Animations** 按鈕將動畫速度減慢 5 倍。如果您想更精確地控制速度,也可以 以程式設計方式完成。

是第一個幀緩慢,還是整個動畫都緩慢?如果是整個動畫,是剪裁導致了減速嗎?也許有另一種繪製場景的方式而不使用剪裁。例如,將不透明的角疊加到正方形上,而不是剪裁成圓角矩形。如果是一個靜態場景正在進行淡入、旋轉或其他操作,RepaintBoundary 可能會有幫助。

檢查螢幕外圖層

#

saveLayer 方法是 Flutter 框架中最耗費資源的方法之一。它在對場景應用後處理時很有用,但它會降低應用的效能,如果不需要,應避免使用。即使您不顯式呼叫 saveLayer,也可能會代表您隱式呼叫,例如在指定 Clip.antiAliasWithSaveLayer(通常作為 clipBehavior)時。

例如,您可能有一組使用 saveLayer 渲染的具有透明度的物件。在這種情況下,將透明度應用於每個單獨的 widget 可能比應用於 widget 樹中更高的父 widget 更有效。對於其他可能耗費資源的操作,如剪裁或陰影,也是如此。

當遇到對 saveLayer 的呼叫時,問自己這些問題:

  • 應用是否需要此效果?
  • 是否可以消除其中任何一次呼叫?
  • 我能否將相同的效果應用於單個元素而不是一個組?

檢查未快取的影像

#

使用 RepaintBoundary 快取影像是好的,當它有意義的時候

從資源角度來看,最耗費資源的操作之一是使用影像檔案渲染紋理。首先,壓縮影像從持久儲存中獲取。影像被解壓縮到主機記憶體(GPU 記憶體),然後傳輸到裝置記憶體(RAM)。

換句話說,影像 I/O 可能很耗費資源。快取提供了複雜層級的快照,以便在後續幀中更容易渲染。由於光柵快取條目構建成本高昂且佔用大量 GPU 記憶體,因此僅在絕對必要時才快取影像。

其他資源

#

以下資源提供了有關使用 Flutter 工具和在 Flutter 中進行除錯的更多資訊: