什麼是ANR
ANR是Android中一個比較有代表性的異常,它的英文全稱是Application Not Responding,就是應用無響應的意思。Android開發中如果主線程(UI線程)在規定的時間內沒有處理完相應的工作就會發生ANR。ANR的直觀感受就是操作應用的過程中明顯的卡頓,主要的原因是在主線程中進行了太多的耗時操作。
ANR產生的原因
當應用的主線程的響應超時纔會引發ANR,那麼產生響應超時的原因可分爲兩種,第一種是當前事件沒有機會得到處理,第二種是當前事件正在處理,但由於耗時太長沒能及時完成任務。
ANR出現的幾種情況
本質上講產生ANR的原因分三種,對應着Activity/View、BroadcastReceiver、Service;
keyDispatchTimeOut
這是最典型也是最常見的一種類型,原因就是View的點擊事件或觸摸事件在特定的時間(5s)內沒有得到響應;
BroadcastTimeOut
BroadcastReceiver的onReceiver()函數運行在主線程中,在特定的時間(10s)內沒有完成處理;
ServiceTimeOut
比較少出現的一種類型,Service的各個生命週期函數在特定時間(20s)內無法得到處理引起;
造成ANR問題的原因(產生場景)
1.主線程在做一些耗時操作,例如在主線程中進行網絡請求、數據庫操作或者文件操作可能會導致UI線程無法及時處理用戶的輸入信息;
2.主線程被其他線程鎖。應用程序在UI線程等待子線程釋放某個鎖,導致無法處理用戶的輸入;;
3.CPU被其他進程佔用,該進程沒被分配到足夠的cpu資源。例如耗時的動畫需要大量的計算工作導致CPU負載過重;;
發生ANR後判斷產生原因很關鍵,到底是因爲主線程進行了耗時操作、主線程被其他線程鎖還是CPU被其他進程佔用,該進程沒被分配到足夠的CPU資源。我們需要從這些角度判斷引起異常的原因然後針對性處理。
ANR的定位和分析
ANR定位
應用發生ANR的時候系統會收集ANR相關的信息內容提供給開發者用來處理問題。
在Logcat中有ANR的相關信息,收集ANR的CPU的使用情況還有trace信息,也就是當前各個線程執行情況。
trace文件保存在Android應用目錄/data/anr/traces.txt中,ANR進程打印出的Log信息也有一定的參考價值。
ANR分析
1.從log中找到ANR發生的信息;
2.在該條log之後會有CPU usage的信息,表明了CPU在ANR前後的用量,從各種CPU Usage信息中分析;
(1)如果某些進程的CPU佔用百分比較高,發生ANR的進程CPU佔用爲0%或非常低,則認爲CPU資源被佔用,進程沒有被分配足夠的資源,從而發生了ANR。這種情況多數可以認爲是系統狀態的問題,並不是由本應用造成。
(2)如果發生ANR的進程CPU佔用比較高,很大可能是因爲應用內一些代碼不合理消耗掉了CPU資源,例如出現了死循環或者後臺有許多線程執行任務等原因。
(3)如果CPU總用量不高,該進程和其他進程的佔用過高,這有一定概率是由於某些主線程的操作就是耗時過長,或者由於主進程被鎖造成的。
3.確定問題需要進一步分析trace文件,該文件記錄了發生ANR前後該進程的各個線程的stack,特別需要注意的是主線程的stack;
(1)主線程是running或者native而對應的棧對應了我們應用中的函數,則很有可能就是執行該函數的時候發生了超時;
(2)主線程被block非常明顯的線程被鎖,這時候可以看是被哪個線程鎖了,考慮優化代碼。如果是死鎖問題就需要及時解決。
(3)由於抓trace的時刻有可能耗時操作已經執行完了,這時候trace就沒什麼用了,這種情況很可能是由於進程的其他線程消耗掉了CPU的資源,這就需要分析其他線程的trace以及ANR前後該進程自己輸出的log了。
降低ANR產生概率
1.不在主線程(UI線程)進行耗時操作;
2.不在BroadcastReceiver的onReceiver()方法中進行耗時操作;
3.各個組件的聲明周期函數都不應該有太耗時的操作;
4.儘量避免主線程被鎖的情況,在一些同步的操作主線程有可能被鎖,需要等待其他線程釋放響應鎖才能繼續執行,這樣會一定的ANR風險,對於這種情況有時也可以用異步線程來執行響應的邏輯。另外要避免死鎖的發生