Android探究之ANR

什麼是ANR

ANR:Application Not Responding,即應用程序無響應。數據庫

在Android中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會監測應用程序的響應時間,若是應用程序主線程(即UI線程)在超時時間內對輸入事件沒有處理完畢,或者對特定操做沒有執行完畢,就會出現ANR。json

對於輸入事件沒有處理完畢產生的ANR,Android會顯示一個對話框,提示用戶當前應用程序沒有響應,用戶能夠選擇繼續等待或者關閉這個應用程序(也就是殺掉這個應用程序的進程)。網絡

爲何會產生ANR

ANR通常有三種類型:app

  1. 輸入事件(按鍵和觸摸事件)5s內沒被處理:Input event dispatching timed out
  2. BroadcastReceiver的事件(onRecieve方法)在規定時間內沒處理完(前臺廣播爲10s,後臺廣播爲60s):Timeout of broadcast BroadcastRecord
  3. Service前臺20s後臺200s未完成啓動:Timeout executing service
  4. ContentProvider的publish在10s內沒進行完:timeout publishing content providers

ANR產生的常見緣由:異步

  1. 主線程(UI線程)在作一些阻塞耗時的工做。例如文件讀寫,數據庫讀寫,網絡查詢等。
  2. 主線程被其餘線程鎖。
  3. cpu被其餘進程佔用,該進程沒被分配到足夠的cpu資源。

如何分析ANR

從log中找到ANR發生的信息

能夠從log中搜索「ANR in」或「am_anr」,會找到ANR發生的log,該行會包含了ANR的時間、進程、是何種ANR等信息。ide

分析ANR產生的trace文件

ANR產生時,系統會生成一個traces.txt的文件放在/data/anr/下。 能夠經過adb命令將其導出到本地:
adb pull data/anr/traces.txt函數

trace文件記錄了發生ANR先後該進程的各個線程的stack。oop

分析思路
  1. 普通阻塞致使的ANR。
  2. 若是是BroadcastReceiver的ANR能夠懷疑BroadCastReceiver.onRecieve()的問題,若是的Service或Provider就懷疑是否其onCreate()的問題。
  3. 若是某些進程的CPU佔用百分比較高,幾乎佔用了全部CPU資源,而發生ANR的進程CPU佔用爲0%或很是低,則認爲CPU資源被佔用,進程沒有被分配足夠的資源,從而發生了ANR。這種狀況多數能夠認爲是系統狀態的問題,並非由本應用形成的。
  4. 若是CPU使用量不多,說明主線程被BLOCK了,多是主進程被鎖。
  5. 若是IOwait很高,說明ANR有多是主線程在進行I/O操做形成的。
  6. 若是CPU使用量接近100%,說明CPU滿負荷,有多是CPU飢餓致使了ANR。
  7. 內存緣由。

如何避免ANR

1.合理使用UI主線程,耗時操做放入其餘線程工做。post

1.1 UI線程儘可能只作跟UI相關的工做。spa

1.2 耗時的工做(好比數據庫操做,I/O,鏈接網絡或者別的有可能阻礙UI線程的操做)把它放入單獨的線程處理。

2.儘可能用Handler來處理UI thread和別的thread之間的交互。

3.合理使用並遵循Android生命週期,避免在onCreate()和onResume()作過多的事情。

4.sharedPreference的使用:

4.1 sharedPreference的commit()方法是同步的,apply()方法通常是異步執行的。在主線程不要用其commit(),用apply()替換。

4.2 sharedPreference的寫是全量寫而非增量寫,因此儘可能都修改完同一apply,避免改一點apply一次(apply()方法在Activity stop的時候主線程會等待寫入完成,提交屢次就很容易卡)。而且存儲文本也不宜過大,這樣會很慢。另外,若是寫入的是json或者xml,因爲須要加和刪轉義符號,速度會比較慢。

5.若是主線程阻塞,開闢單獨的子線程來處理耗時阻塞事務。

6.若是I/O阻塞,通常來講就是文件讀寫或數據庫操做執行在主線程了,能夠經過開闢子線程的方式異步執行。

7.若是內存不夠用,增大VM內存,使用largeHeap屬性,排查內存泄露。

拓展

哪些地方是執行在主線程的

各個組件的生命週期函數都不該該有太耗時的操做。

Activity的全部生命週期回調、Service的onCreate()、BroadcastReceiver的onReceive(開個IntentService去執行相應操做)、
ContentProvider的onCreate()是執行在主線程的。

沒有使用子線程的looper的Handler的handleMessage,
post(Runnable)是執行在主線程的。

AsyncTask的回調中除了doInBackground,其餘都是執行在主線程的。

View的post(Runnable)是執行在主線程的。

儘可能避免主線程的被鎖的狀況。

一些同步的操做主線程有可能被鎖,須要等待其餘線程釋放相應鎖才能繼續執行,這樣會有必定的ANR風險。對於這種狀況有時也能夠用異步線程來執行相應的邏輯。另外, 咱們要避免死鎖的發生(主線程被死鎖基本就等於要發生ANR了)。

相關文章
相關標籤/搜索