1. 程式人生 > >Android探究之ANR

Android探究之ANR

什麼是ANR

ANR:Application Not Responding,即應用程式無響應。

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

對於輸入事件沒有處理完畢產生的ANR,Android會顯示一個對話方塊,提示使用者當前應用程式沒有響應,使用者可以選擇繼續等待或者關閉這個應用程式(也就是殺掉這個應用程式的程序)。

為什麼會產生ANR

ANR一般有三種類型:

  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等資訊。

分析ANR產生的trace檔案

ANR產生時,系統會生成一個traces.txt的檔案放在/data/anr/下。 可以通過adb命令將其匯出到本地:
adb pull data/anr/traces.txt

trace檔案記錄了發生ANR前後該程序的各個執行緒的stack。

分析思路
  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主執行緒,耗時操作放入其他執行緒工作。

1.1 UI執行緒儘量只做跟UI相關的工作。

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了)。