除錯 Flutter 應用

有很多工具和特性可以幫助除錯 Flutter 應用程式,如下列舉了一些:

  • 開發者工具,是一套執行在瀏覽器的效能及分析工具。

  • Android Studio/IntelliJVS Code(藉助 Flutter 和 Dart 外掛)支援內建的原始碼偵錯程式,可以設定斷點,單步除錯,檢查數值。

  • Flutter inspector,是開發者工具提供的 widget 檢查器,也可直接在 Android Studio 和 IntelliJ 中使用(藉助 Flutter 外掛)。檢查器可以視覺化展現 widget 樹,檢視單個 widget 及其屬性值,開啟效能圖層,等等。

開發者工具

要除錯及分析應用,開發者工具可能是你的首選。開發者工具執行在瀏覽器,支援以下特性:

  • 原始碼偵錯程式

  • widget 檢查器,展示視覺化的 widget 樹; “widget select” 模式,在應用中選擇一個 widget,會在 widget 樹直接定位到它的位置。

  • 記憶體分析

  • 時間線檢視,支援追蹤,匯入及匯出追蹤資訊

  • 日誌檢視

如果你在 debug 模式profile 模式 執行,那麼可以在瀏覽器開啟開發者工具連線到你的應用。開發者工具不能用在以 release 模式 編譯的應用,因為除錯和分析資訊都被刪除了。

如果你要用開發者工具分析應用,需確保使用 效能模式。否則,分析的主要輸出將會是用於驗證框架中各種不變式的除錯斷言(檢視 debug 模式斷言)。

GIF showing DevTools features

想獲取更多資訊,請檢視 開發者工具 文件。

設定斷點

要設定斷點,可以直接在 IDE 或編輯器(比如 Android Studio/IntelliJVS Code)、 開發者工具偵錯程式 設定,或者 透過編碼的方式設定

Dart 分析器

如果你使用的是 Flutter 推薦的 IDE 或編輯器,則自帶的 Dart 分析器預設會檢查程式碼,平行處理現可能的錯誤。

如果你使用命令列,則可以使用 flutter analyze 檢查程式碼。

Dart 分析器非常依賴你在程式碼中新增的型別註解,以幫助追蹤問題。建議您在各個地方都加上註解(避免 var,無型別引數,無型別 list 字面量,等等),因為這是追蹤問題最快且最不痛苦的方式。

想獲取更多資訊,請檢視 使用 Dart 分析器

日誌

另一個有用的除錯工具是日誌。透過 編碼 配置日誌,然後在開發者工具中的 日誌檢視 或控制檯檢視輸出。

除錯應用層

Flutter 採用分層架構,包括 widget、渲染和繪製等層。想獲取更多資訊和影片,請檢視 GitHub wiki 上的 The Framework architecture,和社群文章 The Layer Cake

Flutter widget 檢查器提供了 widget 樹的視覺展現,如果你想要更多細節,或關於 wiget、層級或渲染樹的詳盡文字轉儲,請檢視 新增輸出程式碼的方式除錯 Flutter 應用 頁面的 除錯標誌:應用層 部分。

Debug 模式斷言

在開發過程中,強烈建議您使用 Flutter 的 debug 模式。如果你是用 Android Studio 的 bug 圖示執行,或者在命令列執行 flutter run,則預設會使用 debug 模式。有些工具透過 --enable-assets 命令列標誌可以支援斷言陳述式。

在此模式,Dart 斷言陳述式被開啟, Flutter 框架在執行時會計算每一個遇到的斷言陳述式的引數,當結果是 false 時丟擲例外。如此一來,開發者可以控制不變式檢查的開啟或關閉,相應的效能損耗將只發生在除錯期間。

有不變式被違反時,它會被報告給控制檯,並攜帶一些幫助追蹤問題源的上下文資訊。

想獲取更多資訊,請檢視 探索 Dart 語言 中的 斷言 部分。

除錯動畫

除錯動畫最簡單的方法是讓它們變慢。 Flutter inspector 提供一個 放慢動畫(Slow Animations) 的按鈕,你也可以 在程式碼中放慢動畫

想獲取更多關於除錯動畫卡頓的資訊,請檢視 Flutter 效能分析

Measuring app startup time

測量應用啟動時間

要收集有關 Flutter 應用程式啟動所需時間的詳細資訊,可以在執行 flutter run 時使用 trace-startupprofile 選項。

$ flutter run --trace-startup --profile

追蹤輸出被儲存到 Flutter 工程目錄在 build 目錄下,一個名為 start_up_info.json 的 JSON 檔案中。輸出列出了從應用程式啟動到這些追蹤事件(以微秒捕獲)所用的時間:

  • 進入 Flutter 引擎時

  • 展示應用第一幀時

  • 初始化Flutter框架時

  • 完成Flutter框架初始化時

例如:

{
  "engineEnterTimestampMicros": 96025565262,
  "timeToFirstFrameMicros": 2171978,
  "timeToFrameworkInitMicros": 514585,
  "timeAfterFrameworkInitMicros": 1657393
}

Tracing Dart code

追蹤 Dart 程式碼效能

要進行效能追蹤,你可以使用開發者工具的 時間線檢視。時間線檢視還支援匯入和匯出追蹤檔案。想要獲取更多資訊,請檢視 時間線檢視

你也可以 在程式碼中追蹤,不過這些追蹤資訊無法匯入到開發者模式的時間線檢視。

追蹤時請確保在 效能模式 執行應用,這樣才能保證執行時效能特徵同你最終產品高度一致。

效能圖層

要圖形化展現你應用的效能,可以開啟效能圖層。你可以在 Flutter inspector 中點選 Performance Overlay 按鈕。

你也可以 在程式碼中 開啟該圖層。

關於如何解析圖層中的圖形,請檢視 Flutter 效能分析 中的 效能圖層 部分。

除錯標誌

大部分情況,你不需要直接使用除錯標誌,因為可以在 開發者工具 找到最有用的除錯功能。但是如果你偏好直接使用除錯標誌,請檢視 新增輸出程式碼的方式除錯 Flutter 應用 中的 除錯標誌:效能 部分。

常見問題

下面是一些在 macOS 上遇到的問題。

“控制代碼數超出系統限制” 例外 (macOS)

mac OS 在同一時間可以開啟多少控制代碼的預設限制數相當低。如果你達到這個極限,可以用 ulimit 命令增加可用控制代碼的數量:

ulimit -S -n 2048

如果您使用 Travis 或 Cirrus 進行測試,請透過在 flutter/.travis.yml 或 flutter/.cirrus.yml 中增加同樣的命令來增加它們可以開啟的控制代碼數量。

被標記為 const 的相同 Widget 應被視為同一物件,然而卻並沒有

在 debug 模式下,(由於 Dart 的常量去重策略)你也許會發現兩個 const 的 widget 長得並不完全一樣。

例如,下面的程式碼應該列印 1:

print(<Widget>{
  // this is the syntax for a Set<Widget> literal
  const SizedBox(),
  const SizedBox(),
}.length);

這段程式碼應該列印 1(而不是 2),這是由於兩個常量相同且在同一個 set 中(實際上分析器抱怨 “集合文字中的兩個元素不應相等”)。正如我們所期待的那樣,在 release 模式下建構的時候,它確實列印了 1。然而,在 debug 模式下它卻列印了 2。這是由於 Flutter tool 在編譯期向 Widget 的構造器注入了源位置,所以下面的程式碼有效:

print(<Widget>{
  const SizedBox(/* location: Location(file: 'foo.dart', line: 12) */),
  const SizedBox(/* location: Location(file: 'foo.dart', line: 13) */),
}.length);

上面的程式碼在結果中的例項不同,故它們在 set 中並沒有重複。我們使用注入資訊彙報相關 widget 的建立資訊,使得 widget 出現例外時錯誤訊息會更加清晰。不幸的是,它會導致相同常量在編譯期變為不同例項。

要關閉此行為,請在執行 flutter run 命令的同時傳 --no-track-widget-creation。有了這個標記,程式碼將會在 debug 和 release 模式下列印 1,而錯誤訊息這邊會有一條訊息說,除非開啟 widget 建立追蹤器,否則我們將無法提供完整的資訊。

你也可以檢視:

其他資源

以下是其他一些有用的文件: