軟體開發階段「偵錯、除錯」是重要的環節。通過偵錯我們修正小錯誤,修正奇怪的效果和邏輯等等。
Flutter 支援了多種工具協助我們進行偵錯,而最簡單的方式就是通過 IDE 進行。
為了在 IDE 中進行偵錯,首先我們需要在 IDE 中運行我們的應用程式。如果你使用的是 Visual Studio Code 那麼在介面左邊你會看到「執行與偵錯 (RUN AND DEBUG) 」,切換到該功能,左邊區塊的上方可以選擇專案(建立 launch.json
)並開始執行偵錯。
這個 launch.json
設定可以設定 main.dart
作為進入點又或者你的專案調整了預設的路徑。
"program": "lib/main.dart"
如果你使用的是 Android Studio 那麼只要使用介面上方的偵錯模式即可。
一旦開啟這種模式,我們可以加入中斷點觀察執行下的情況。大部分的 IDE 我們只要在程式碼行數左邊點擊就可以加入中斷點。
Android Studio
Visual Studio Code
如你所見,這些功能讓我們可以輕易的查看程式發生了什麼事,了解問題的原因,上面我們稍微介紹了 Visual Studio Code 的介面,但 Android Studio 或其他官方推薦的 IDE 也都具備類似的功能,你可以選擇偏好的工具。
有一點需要注意的是,遇到非同步程式碼時,我們可能要額外設定中斷點,以便在非同步完成時攔截到執行流程。非同步的情況可能導致呼叫堆疊不完整,導致不好理解問題,但總歸來說這些工具確實可以提供一些幫助。你應該花些時間熟悉選擇的工具。
除了偵錯功能, 2024 的現在 IDE 也支援各種方便好用的 AI 功能。這裡推薦 https://www.cursor.com/ 非常值得一試。個人非常強烈推薦,我個人已經取消 Github Coplit 改用 Cursor 了。
Dart 也支援其他進階的測試功能協助我們處理一些比較複雜的情況
debugger()
也被稱為程式化中斷點,類似於 Laravel 中的 dd()
,Ruby 的 binding.pry
,Go 的 runtime.Breakpoint()
void login(String username, String password) {
debugger(when: password == null);
}
上面範例,中斷只有在條件符合的時候才會發生,也就是密碼為 null 的時候中斷。對於查找非預期邏輯錯誤來說非常實用。
print()
這個方法可以協助我們在 Flutter Console 中輸出一些資訊,也就是執行 flutter run
時。另外,還有一個 debugPring()
類似於 print()
但會限制輸出訊息的頻率避免在 Android 出現資料丟失的情況
assert()
當條件不符合時,用來中斷應用程式的執行。類似於 debugger()
但不同的是它不是暫停執行,而是拋出 AssertionError
來中斷
Widget build(BuildContext context) {
assert(context != null, 'context 不得為 null');
//
}
協助我們實踐 Fail Fast 快速失敗原則,可以避免後續可能出現的一系列難以追踪的錯誤。
值得一提的是,debugger()
和 assert()
在正式環境會直接被忽略。
DevTool 即協助性能分析調效和偵錯的工具集合。在 Dart 和 Flutter 中這些工具可以通過瀏覽器存取,或者直接使用和 IDE 整合的版本。
開啟偵錯模式之後我們可以看到自動開啟的檢測工具。可以提供類似瀏覽器檢測工具的功能協助我們檢測組件結構等,我們也可以調整組件的屬性看看呈現的效果。
Overlay guidelines 按鈕會在螢幕上加上佈局規則,我們可以了解組件是依據什麼方式定位到該位置。我們可以看到箭頭指示看看佈局了解組件之間的關係和邊界。同樣的 Show baselines 按鈕可以開啟顯示文字是如何定位的。
Show borders 和 Highlight images 可以協助我們找出過度重新渲染或耗費記憶體區塊以協助改善效能。
動畫部分支援「5倍慢速運行動畫」可以讓我們查看動畫效果是否如我們所預期。
Toggle select widget mode 可以開啟選取模式我們可以直接在模擬器上點擊找到組件。
在我們執行預設偵錯模式時,運行效能和正式版本並不相同。我們知道 Flutter 在偵錯模式使用 JIT 編譯器,而正式版本和效能分析模式程式碼會使用 AOT 預先編譯。
為了分析效能,我們須確保應用程式在其最佳情境下運行,這也是為何 Flutter 提供了不同的執行方式。
在效能分析模式下,應用程式編譯方式和正式版本非常相似。
另一個重點,效能分析需要使用實體裝置。模擬器無法反映真實世界設備的性能。由於裝置的不同,測試的結果可能會受到影響。
要執行效能分析模式我們只需要加入 --profile
參數。請記住,這只在搭配實機可用。
$ flutter run --profile
執行該模式我們可以取得一般所需效能分析資訊。
若你使用的編輯器是 Visual Studio Code 那麼你可以在偵錯區塊點擊設定開啟 launch.json
檔案,或者在 .vscode
目錄下找到該檔案。
"configurations": [
{
"name": "test_demo",
"request": "launch",
"type": "dart",
"program": "lib/main.dart"
},
{
"name": "test_demo (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "test_demo (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
上面的設定包含了各種模式,其中關鍵參數就是 flutterMode
。
一旦啟動效能分析模式你會注意到我們偵錯工具列上多了一個效能的頁面。這些資訊可以協助你找到效能瓶頸,防止記憶體洩漏等。
效能分析模式下我們還可以開啟「Performance overlay」圖,這個圖會在應用程式畫面上方顯示進一步在你操作時即時提供關於效能分析的資料 - 主要是渲染的時間。
兩個圖分別表示 UI 執行緒和 Raster 執行訓渲染幀所需的時間。當前幀由比較高的綠線表示。我們可以看到最近 300 幀的情況對渲染有個大概的了解。UI 執行緒負責構建樹狀結構,Raster 負責將結構轉換為實際的像素。
UI 執行緒就是 Dart 程式碼執行的地方,包含執行應用程式所需 Flutter 框架程式碼。程式中的使用者介面如之前提及的都會被轉換成一個樹狀結構,由此發送渲染指令到 Raster 執行緒。
這個執行緒接收結構並和 GPU 搭配將指令繪製到螢幕上。原本這個執行緒使用 Skia 引擎來渲染,新版本將使用 Impeller 。基本上我們不會和這個執行緒打交道,我們主要處理 UI 執行緒裡面發生的事情。
除了上面兩個主要執行緒之外 Flutter 還包含平台執行緒例如插件的程式碼、 I/O ,一些耗費效能的任務會在這個執行緒處理。
在 Flutter 開發過程中,掌握有效的偵錯和效能分析技巧非常重要。從 IDE中 的基本偵錯功能,到使用debugger()
、print()
和 assert()
等工具,再到利用 DevTools 和 Performance Overlay 進行深入的效能分析,Flutter提供了豐富的工具集來幫助開發者識別和解決問題。
雖然在學習曲線上 React Native 的概念確實相對容易理解,但 Flutter 的 DX 和其它優勢值得投入。