iT邦幫忙

2022 iThome 鐵人賽

DAY 3
0
Mobile Development

Android app 效能優化系列 第 3

ANR 應用程式沒有回應

  • 分享至 

  • xImage
  •  

若主執行緒( Main threat、UI thread ) 處於阻塞的情況,就有可能會觸發 ANR ( Application Not Responding ) 錯誤(如下圖)。這種錯誤是最嚴重的,也會帶來糟糕的使用體驗,當使用者遇到這種情況也只能離開 app。

ANR

常見的 ANR 發生原因有:

  1. 在主執行緒處理耗時的工作
  2. 主執行緒正在等待的資源被其他執行緒鎖定
  3. 在 BroadcastReceivers 處理耗時的工作

在主執行緒處理耗時的工作

ANR 最常發生的原因是在主執行緒處理耗時的工作。下方程式碼,我們在主執行緒以 Thread.sleep(2000) 來模擬正在執行一件耗時 2秒的工作。

binding.send.setOnClickListener{
	Thread.sleep(2000)
}

從 Log 可以看到有 179個 frames 被跳過了,也就是你的 UI 在這 2 秒的期間是卡住。

Skipped 179 frames!  The application may be doing too much work on its main thread.

當執行工作的時間再久一點到 10 秒時,就會出現 ANR 錯誤了。

binding.send.setOnClickListener{
	Thread.sleep(10000)
}
ANR in evan.chen.toturial.anrsample (evan.chen.toturial.anrsample/.MainActivity)

主執行緒正在等待的資源被其他執行緒鎖定

為了解決 執行緒間的 race condition , 我們會使用 synchronized 來確保一次只有一個執行緒能存取。在這個例子,雖然已經把耗時的工作放到背景執行緒,但主執行緒在等待的資源被鎖定,仍會造成 ANR,因為這也代表著主執行緒正在等待中。

下方的式程碼說明了:
button1 點擊時,在背景執行緒因為用了 synchronized 鎖定了resource。
button2 點擊時,resource 已被另一個執行緒給鎖定,而主執行緒需要這個被鎖定的 resource 才能完成工作,這也造成了 ANR。

private var resource: String = ""

//button1點擊,在背景執行緒鎖定了 resource
binding.button1.setOnClickListener {
    Thread {
        synchronized(resource) {
            Thread.sleep(10000)
        }
    }.start()
}

//button2點擊,需等待被另一個執行緒鎖定的 resouce
binding.button2.setOnClickListener {
    synchronized(resource) {
        println("OK:$resource")
    }
}

在BroadcastReceivers 處理耗時的工作

在 BroadcastReceiver 是不允許執行耗時的工作,取而代之的是收到廣播後使用 WorkManager 來處理耗時工作。

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent?) {
        //用Thread.sleep來模擬做耗時的事
        Thread.sleep(10000)
    }
}

讓應用程式維持在正常的回應速度

這篇我們介紹如何避免 ANR,在 UI 的回應超過 5 秒沒有回應就會發生ANR,但其實使用者與 UI 的互動在0.1~0.2秒就應該有所回應。在後續的 Layout 效能篇,我們會再做深入的介紹。

參考:
https://developer.android.com/topic/performance/vitals/anr


上一篇
從背景處理優化效能
下一篇
使用 StrictMode 找出在主執行緒的異常請求
系列文
Android app 效能優化30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言