Android探究之ANR

什么是ANR


ANR:Application Not Responding,即應(yīng)用程序無響應(yīng)。

在Android中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會監(jiān)測應(yīng)用程序的響應(yīng)時間,如果應(yīng)用程序主線程(即UI線程)在超時時間內(nèi)對輸入事件沒有處理完畢,或者對特定操作沒有執(zhí)行完畢,就會出現(xiàn)ANR。

對于輸入事件沒有處理完畢產(chǎn)生的ANR,Android會顯示一個對話框,提示用戶當(dāng)前應(yīng)用程序沒有響應(yīng),用戶可以選擇繼續(xù)等待或者關(guān)閉這個應(yīng)用程序(也就是殺掉這個應(yīng)用程序的進(jìn)程)。

為什么會產(chǎn)生ANR


ANR一般有三種類型:

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

ANR產(chǎn)生的常見原因:

  1. 主線程(UI線程)在做一些阻塞耗時的工作。例如文件讀寫,數(shù)據(jù)庫讀寫,網(wǎng)絡(luò)查詢等。
  2. 主線程被其他線程鎖。
  3. cpu被其他進(jìn)程占用,該進(jìn)程沒被分配到足夠的cpu資源。

如何分析ANR


從log中找到ANR發(fā)生的信息

可以從log中搜索“ANR in”或“am_anr”,會找到ANR發(fā)生的log,該行會包含了ANR的時間、進(jìn)程、是何種ANR等信息。

分析ANR產(chǎn)生的trace文件

ANR產(chǎn)生時,系統(tǒng)會生成一個traces.txt的文件放在/data/anr/下。 可以通過adb命令將其導(dǎo)出到本地:
adb pull data/anr/traces.txt

trace文件記錄了發(fā)生ANR前后該進(jìn)程的各個線程的stack。

分析思路
  1. 普通阻塞導(dǎo)致的ANR。
  2. 如果是BroadcastReceiver的ANR可以懷疑BroadCastReceiver.onRecieve()的問題,如果的Service或Provider就懷疑是否其onCreate()的問題。
  3. 如果某些進(jìn)程的CPU占用百分比較高,幾乎占用了所有CPU資源,而發(fā)生ANR的進(jìn)程CPU占用為0%或非常低,則認(rèn)為CPU資源被占用,進(jìn)程沒有被分配足夠的資源,從而發(fā)生了ANR。這種情況多數(shù)可以認(rèn)為是系統(tǒng)狀態(tài)的問題,并不是由本應(yīng)用造成的。
  4. 如果CPU使用量很少,說明主線程被BLOCK了,可能是主進(jìn)程被鎖。
  5. 如果IOwait很高,說明ANR有可能是主線程在進(jìn)行I/O操作造成的。
  6. 如果CPU使用量接近100%,說明CPU滿負(fù)荷,有可能是CPU饑餓導(dǎo)致了ANR。
  7. 內(nèi)存原因。

如何避免ANR


1.合理使用UI主線程,耗時操作放入其他線程工作。

1.1 UI線程盡量只做跟UI相關(guān)的工作。

1.2 耗時的工作(比如數(shù)據(jù)庫操作,I/O,連接網(wǎng)絡(luò)或者別的有可能阻礙UI線程的操作)把它放入單獨的線程處理。

2.盡量用Handler來處理UI thread和別的thread之間的交互。

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

4.sharedPreference的使用:

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

4.2 sharedPreference的寫是全量寫而非增量寫,所以盡量都修改完同一apply,避免改一點apply一次(apply()方法在Activity stop的時候主線程會等待寫入完成,提交多次就很容易卡)。并且存儲文本也不宜過大,這樣會很慢。另外,如果寫入的是json或者xml,由于需要加和刪轉(zhuǎn)義符號,速度會比較慢。

5.如果主線程阻塞,開辟單獨的子線程來處理耗時阻塞事務(wù)。

6.如果I/O阻塞,一般來說就是文件讀寫或數(shù)據(jù)庫操作執(zhí)行在主線程了,可以通過開辟子線程的方式異步執(zhí)行。

7.如果內(nèi)存不夠用,增大VM內(nèi)存,使用largeHeap屬性,排查內(nèi)存泄露。

拓展


哪些地方是執(zhí)行在主線程的

各個組件的生命周期函數(shù)都不應(yīng)該有太耗時的操作。

Activity的所有生命周期回調(diào)、Service的onCreate()、BroadcastReceiver的onReceive(開個IntentService去執(zhí)行相應(yīng)操作)、
ContentProvider的onCreate()是執(zhí)行在主線程的。

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

AsyncTask的回調(diào)中除了doInBackground,其他都是執(zhí)行在主線程的。

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

盡量避免主線程的被鎖的情況。

一些同步的操作主線程有可能被鎖,需要等待其他線程釋放相應(yīng)鎖才能繼續(xù)執(zhí)行,這樣會有一定的ANR風(fēng)險。對于這種情況有時也可以用異步線程來執(zhí)行相應(yīng)的邏輯。另外, 我們要避免死鎖的發(fā)生(主線程被死鎖基本就等于要發(fā)生ANR了)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在實際情況中,當(dāng)Android項目的用戶量特別大時候,一些細(xì)小的問題也會被放大,ANR問題就是一個典型的例子。一些...
    Uprising閱讀 54,340評論 4 116
  • 面試必背 會舍棄、總結(jié)概括——根據(jù)我這些年面試和看面試題搜集過來的知識點匯總而來 建議根據(jù)我的寫的面試應(yīng)對思路中的...
    luoyangzk閱讀 7,168評論 6 173
  • 所有知識點已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,710評論 1 4
  • LP7 激情飛揚.超越一切團(tuán)隊 總教練:林堅 教練.潘饒平 隊員:張景軒 我的宣誓:我是一個自信負(fù)責(zé)任的男人 我...
    為夢想而起航閱讀 114評論 0 0
  • 2017已經(jīng)揮手告別了,感恩這一路走來所有的朋友,給予我的關(guān)心,幫助,陪伴!在此衷心的說一聲,謝謝! 2018已經(jīng)...
    創(chuàng)客田野閱讀 271評論 0 0

友情鏈接更多精彩內(nèi)容