只要是軟體使用者,很可能都曾遇到錯誤。「
bug
」是指某個軟體導致錯誤的行為,例如應用程式當機或功能無法正常運作。無論經驗為何,所有開發人員在撰寫程式碼時都會回報錯誤,而 Android 開發人員最重要的技能之一就是辨識及修正。
bug的修正程序稱為偵錯(debugging)
。知名電腦科學家 Brian Kernighan 曾表示,「最有效的偵錯工具至今仍在審慎考量,加上眾所皆知的印刷品聲明。」 您可能已經很熟悉先前程式碼研究室的 Kotlin’s println() 陳述式,但專業的 Android 開發人員會使用記錄功能來更妥善地整理程式的輸出內容。在這個程式碼研究室中,您將瞭解如何使用 Android Studio 中的記錄功能,以及如何將記錄用於偵錯工具。您將瞭解如何讀取錯誤訊息記錄 (稱為堆疊追蹤),藉此找出並修正錯誤。
學習目標
- 使用
android.util.Logger
寫入記錄檔。 - 瞭解不同記錄檔層級的使用時機。
- 使用記錄是一項簡單的強大工具。
- 如何在堆疊追蹤中尋找有意義的資訊。
- 搜尋錯誤訊息以解決應用程式當機問題。
建立新專案
我們將從空白專案開始,先顯示一個空白專案來示範記錄陳述式及其偵錯用途,而不是使用大型的應用程式。
請先建立新的 Android Studio 專案,如圖所示。
在「New Project」(新增專案) 畫面中,選擇「Empty Views Activity」(空白活動)。
將應用程式命名為「Debugging」。確認語言已設為 Kotlin,且其他維持不變。

對輸出進行記錄與偵錯
在先前的課程中,您曾使用 Kotlin 的 println()
陳述式產生文字輸出。在 Android 應用程式中,記錄記錄的最佳做法是使用 Log
類別。記錄輸出功能有多種函式,採 Log.v()
、Log.d()
、Log.i()
、Log.w()
或 Log.e()
格式。這些方法有兩種參數,第一個稱為「標記」,是識別記錄訊息來源 (例如記錄文字的類別名稱) 的字串。第二個則是實際記錄訊息 。
執行下列步驟,開始在空白專案中使用記錄功能。
- 在
MainActivity.kt
的類別宣告之前,新增名為TAG
的常數,並將該值設為類別的名稱MainActivity
。
1 | private const val TAG = "MainActivity" |
- 向 MainActivity 類別新增一個名為
logging()
的新函數,如下所示。
1 | fun logging() { |
- 在
onCreate()
中呼叫logging()
1 | override fun onCreate(savedInstanceState: Bundle?) { |
執行應用程式以查看記錄。記錄檔隨即顯示在畫面底部的
Logcat
視窗中。在篩選框中輸入level:debug
,藉此排除與您應用程式無關的任何記錄。您應該會在輸出視窗中看到「Hello, world!」。如有需要,請在
Logcat
視窗頂端的搜尋框中輸入「hello」,即可搜尋所有記錄。
記錄層級
造成不同記錄檔函式 (名稱不同) 的原因,原因是這些相對應的記錄層級不同。您可以根據要輸出的資訊類型,在 Logcat
輸出內容中使用不同記錄檔層級進行篩選。您會定期使用五個主要記錄層級。
紀錄層級 | 用途 | 範例 |
---|---|---|
錯誤(ERROR) | 錯誤記錄會回報發生嚴重錯誤,例如應用程式當機的原因。 | Log.e(TAG, “The cake was left in the oven for too long and burned.”). |
警告(WARN) | WARN 記錄較不嚴重,但仍會回報應修正的問題,以避免嚴重的錯誤。舉例來說,如果您呼叫的函式「已淘汰」,我們不建議使用該函式取代較新的函式。 | Log.w(TAG, “This oven does not heat evenly. You may want to turn the cake around halfway through to promote even browning.”) |
資訊(INFO) | INFO 記錄提供實用資訊,例如成功完成的作業。 | Log.i(TAG, “The cake is ready to be served.”).println(“The cake has cooled.”) |
偵錯(DEBUG) | DEBUG 記錄包含調查問題時可能會用到的資訊。這些版本不會顯示在發布版本中,例如您在 Google Play 商店發布的版本。 | Log.d(TAG, “Cake was removed from the oven after 55 minutes. Recipe calls for the cake to be removed after 50 - 60 minutes.”) |
詳細程度(VERBOSE) | 顧名思義,「VERBOSE」是最低的記錄層級。何謂偵錯記錄與詳細記錄其實有點主觀,但一般而言,詳細記錄可以在功能實作之後移除,而偵錯記錄在偵錯時可能仍然有用。這些版本不包含版本。 | Log.v(TAG, “Put the mixing bowl on the counter.”)Log.v(TAG, “Grabbed the eggs from the refrigerator.”)Log.v(TAG, “Plugged in the stand mixer.”) |
請注意,目前並沒有設定各類型記錄層級的使用規則,特別是使用 DEBUG
和 VERBOSE
的時機。軟體開發團隊可能會制定個別記錄層級的使用時機,或是決定不採用特定記錄層級 (例如 VERBOSE)。這兩個記錄層級有一重要的重點是,這些版本並沒有發布版本,因此使用記錄偵錯功能不會影響已發布應用程式的效能,而 println()
陳述式則保留在發布版本中,並對負面影響產生負面影響成效。
讓我們看看 Logcat 中各種不同的記錄層級。
- 在
MainActivity.kt
中,將logging()
方法的內容替換為下列內容。
1 | fun logging() { |
- 執行您的應用程式,並在
Logcat
中觀察輸出內容。輸入tag: MainActivity
,篩選出只顯示含有「MainActivity」代碼的記錄。

使用鍵/值搜尋查詢記錄
在 Android Studio 中,您可以直接透過主查詢欄位產生鍵/值搜尋。這個查詢系統可提供精準的查詢結果,也能根據鍵/值排除記錄。儘管您可以選擇使用規則運算式,但在查詢上並非必要。如要查看建議項目,請在查詢欄位中按下 Ctrl + Space 鍵。

以下提供幾個可用於查詢的鍵範例:
- tag:比對記錄項目的 tag 欄位。
- package:比對記錄應用程式的套件名稱。
- process:比對記錄應用程式的程序名稱。
- message:比對記錄項目的訊息部分。
- level:比對指定或更嚴重的記錄層級,例如 DEBUG。
- age:比對項目時間戳記是否最新。值的指定方式是以數字後面加上代表時間單位的字母:s 代表秒數、m 代表分鐘數、h 代表小時數,d 則代表天數。例如,age: 5m 只會篩選出過去 5 分鐘內記錄的訊息。
更詳細的介紹請點這裡查看。
含有錯誤訊息的記錄
在空白專案中無法偵錯。Android 開發人員經常遇到許多錯誤,那就是應用程式當機,但用戶體驗不佳。讓我們新增一些程式碼,讓這個應用程式當機。
- 將下列函式新增至
logging()
函式的MainActivity.kt
。這個程式碼以兩個數字開頭,並使用repeat
來將分子除以五分子的結果。每次執行repeat
區塊中的程式碼時,分母的值就會減少 1。在第 5 次和最後一次疊代時,應用程式嘗試除以 0。
1 | fun division() { |
- 新增
division()
函式到onCreate()
中的logging()
後。
1 | override fun onCreate(savedInstanceState: Bundle?) { |
- 再次執行應用程式,向下捲動至
MainActivity.kt
類別中的記錄,即可查看先前定義的logging()
函式中的日誌、「division()
」函式的詳細記錄,以及說明應用程式為什麼當機的紅色錯誤記錄。
堆疊追蹤剖析
說明當機的錯誤記錄 (也稱為例外狀況) 亦稱作堆疊追蹤。堆疊追蹤會顯示所有已觸發至例外狀況的函式,而且系統會從最近呼叫的時間開始呼叫。完整輸出內容如下所示。
1 | Process: com.example.debugging, PID: 14581 |
如何查看錯誤
java.lang.RuntimeException:
1 | java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.debugging/com.example.debugging.MainActivity}: java.lang.ArithmeticException: divide by zero |
第一行代表應用程式無法開始活動,這就是應用程式當機的原因。下一行會提供更多資訊。具體來說,該活動無法啟動的原因是 ArithmeticException
。具體來說,ArithmeticException
的類型是「除以 0」。
Caused by:
1 | Caused by: java.lang.ArithmeticException: divide by zero |
如果向下捲動頁面顯示「Caused by:
」一行,表示發生「除以 0
」的錯誤。另會顯示錯誤發生的確切函式 (division()
) 和確切的行數 (21)。Logcat 視窗中的檔案名稱和行數是超連結。輸出結果也會列出發生錯誤的函式名稱 (division()
) 和呼叫該函式的函式 (onCreate()
)。
使用記錄檔找出並修正錯誤
使用記錄陳述式,透過輸出分母值來避免除數為零的情況,從而節省時間。
- 在
Log.v()
之前,新增Log.d()
呼叫記錄分母。Log.d()
的用途是偵錯,因此可用來篩選詳細記錄。
1 | Log.d(TAG, "$denominator") |
- 再次執行應用程式。雖然分母仍持續當機,但分母應該記錄多次。您可以使用篩選條件設定,只顯示含有 “MainActivity” 標記的記錄。

- 您可以看到有多個值輸出。當分母為 0 時,迴圈會在第五次疊代之前執行數次。如要修正錯誤,您可以將迴圈中的疊代次數從 5 變更為 4。再次執行應用程式時,應用程式不會再停止運作。
1 | fun division() { |