Android ANR (Application Not Responding)

當你的 App UI 突然卡住,且幾秒鐘後彈出一個「App 無回應」的對話框時,這就是所謂的 ANR (Application Not Responding)。這對使用者體驗來說是致命的,通常會導致使用者直接解除安裝。

本文將深入探討 ANR 的觸發機制、如何診斷原因以及最佳的預防實踐。

什麼是 ANR?

在 Android 中,系統會監控應用的回應速度。如果應用在特定時間內無法處理輸入事件(如觸控、按鍵)或執行特定的系統回呼,系統就會判定該應用已「失去回應」。

觸發 ANR 的門檻規則

  1. 輸入事件 (Input Dispatching):當觸控或按鍵事件在 5 秒內 未得到回應。
  2. BroadcastReceiver:前台廣播在 10 秒內 未處理完畢。
  3. Service:前台服務在 20 秒內 未啟動完成。

為什麼會發生 ANR?

ANR 的本質是 主執行緒 (Main Thread / UI Thread) 被阻塞 (Blocked)。主執行緒負責處理 UI 更新、繪製與使用者互動。一旦主執行緒在忙於其他事情,就無法即時處理 UI 事件。

常見的阻塞原因

  • 耗時的 I/O 操作:在主執行緒讀取大型檔案或寫入資料庫。
  • 網路請求:直接在主執行緒發起網路連線(雖然現代 Android 已禁止此行為,但在舊代碼中仍可見)。
  • 密集的 CPU 計算:例如進行複雜的圖片模糊處理或大量資料排序。
  • 死鎖 (Deadlock):主執行緒在等待一個被其他執行緒持有的鎖。
  • 未優化的 Layout:過於複雜的佈局導致繪製時間過長。

如何診斷 ANR?

當 ANR 發生時,我們需要找出「是哪一行程式碼卡住了主執行緒」。

1. 檢視 Logcat

當 ANR 發生時,系統會在 Logcat 中輸出相關訊息。搜尋關鍵字 ANRActivityManager,通常能看到導致 ANR 的進程名稱。

2. StrictMode (嚴苛模式)

在開發階段,你可以啟用 StrictMode,當偵測到你在主執行緒進行網路或磁碟操作時,它會讓 App 崩潰或噴出警告。

fun onCreate() {
    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(
            StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()
                .penaltyLog()
                .build()
        )
    }
    super.onCreate()
}

3. 分析 traces.txt

當發送 ANR 時,系統會將所有執行緒的狀態寫入 /data/anr/traces.txt(新版 Android 為 anr_ 開頭的檔案)。 你可以透過 Android Studio 的 Device Explorer 匯出此檔案,查看 main 執行緒當下的呼叫堆疊 (Stack Trace)。

如何預防 ANR?

預防 ANR 的黃金準則就是:永遠不要在主執行緒執行耗時任務。

1. 使用協程 Coroutines

這是目前 Android 開發的首選方案。透過 withContext(Dispatchers.IO) 將耗時任務切換到背景執行緒。

fun loadData() {
    viewModelScope.launch {
        // 在背景執行緒讀取資料庫
        val data = withContext(Dispatchers.IO) {
            repository.fetchHeavyData() 
        }
        // 自動切回主執行緒更新 UI
        uiState.value = data
    }
}

2. 善用 WorkManager

對於需要在 App 關閉後仍繼續執行的長任務(如上傳大型圖片),應使用 WorkManager 而非 Service。

3. 優化資料庫查詢

Room 中,務必使用非同步查詢(回傳 Flowsuspend),並為常用查詢欄位建立索引 (Index)。

總結

ANR 是提醒開發者應更有效率地使用執行緒的信號。保持主執行緒的純粹與輕量,僅處理 UI 更新與簡單的邏輯,是打造流暢 Android 應用程式的不二法門。