目錄:
一、ANR說明和原因
二彩届、ANR分析辦法
三、如何降低ANR的概率
四奖唯、造成ANR的原因及解決辦法
五惨缆、ANR源碼分析
六、Android ANR的信息收集
一、ANR說明和原因
1.1 簡介
ANR全稱:Application Not Responding坯墨,也就是應(yīng)用程序無響應(yīng)寂汇。
1.2 原因
Android系統(tǒng)中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會檢測App的響應(yīng)時間捣染,如果App在特定時間無法響應(yīng)屏幕觸摸或鍵盤輸入時間骄瓣,或者特定事件沒有處理完畢,就會出現(xiàn)ANR耍攘。
以下四個條件都可以造成ANR發(fā)生:
InputDispatching Timeout:5秒內(nèi)無法響應(yīng)屏幕觸摸事件或鍵盤輸入事件
BroadcastQueue Timeout :在執(zhí)行前臺廣播(BroadcastReceiver)的onReceive()函數(shù)時10秒沒有處理完成榕栏,后臺為60秒。
Service Timeout :Service的各個生命周期函數(shù)在特定時間內(nèi)(前臺服務(wù)20s,后臺服務(wù)200s)無法完成響應(yīng)蕾各。
ContentProvider Timeout :ContentProvider的publish在10s內(nèi)沒進(jìn)行完扒磁。
(進(jìn)程啟動過程中,如果發(fā)生會直接殺進(jìn)程以及清理相應(yīng)信息,而不會彈出ANR的對話框)
1.3 避免
盡量避免在主線程(UI線程)中作耗時操作式曲。
那么耗時操作就放在子線程中妨托。
關(guān)于多線程可以參考:Android多線程:理解和簡單使用總結(jié)
二、ANR分析辦法
從前文可以明確吝羞,ANR問題是由于主線程的任務(wù)在規(guī)定時間內(nèi)沒處理完任務(wù)兰伤,而造成這種情況的原因大致會有一下幾點(diǎn):
主線程在做一些耗時的工作
主線程被其他線程鎖
cpu被其他進(jìn)程占用,該進(jìn)程沒被分配到足夠的cpu資源钧排。
判斷一個ANR屬于哪種情況便是分析ANR問題的關(guān)鍵敦腔。那么拿到一個anr的日志,應(yīng)該如何分析呢恨溜?
在發(fā)生ANR的時候符衔,系統(tǒng)會收集ANR相關(guān)的信息提供給開發(fā)者:首先在Log中有ANR相關(guān)的信息,其次會收集ANR時的CPU使用情況筒捺,還會收集trace信息柏腻,也就是當(dāng)時各個線程的執(zhí)行情況纸厉。trace文件保存到了/data/anr/traces.txt中系吭,此外,ANR前后該進(jìn)程打印出的log也有一定價值颗品。一般來說可以按一下思路來分析:
從log中找到ANR反生的信息:可以從log中搜索“ANR in”或“am_anr”肯尺,會找到ANR發(fā)生的log,該行會包含了ANR的時間躯枢、進(jìn)程则吟、是何種ANR等信息,如果是BroadcastReceiver的ANR可以懷疑BroadCastReceiver.onRecieve()的問題锄蹂,如果的Service或Provider就懷疑是否其onCreate()的問題氓仲。
-
在該條log之后會有CPU usage的信息,表明了CPU在ANR前后的用量(log會表明截取ANR的時間),從各種CPU Usage信息中大概可以分析如下幾點(diǎn):
(1). 如果某些進(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)用造成的砌滞。
(2). 如果發(fā)生ANR的進(jìn)程CPU占用較高,如到了80%或90%以上坏怪,則可以懷疑應(yīng)用內(nèi)一些代碼不合理消耗掉了CPU資源贝润,如出現(xiàn)了死循環(huán)或者后臺有許多線程執(zhí)行任務(wù)等等原因,這就要結(jié)合trace和ANR前后的log進(jìn)一步分析了铝宵。
(3). 如果CPU總用量不高题暖,該進(jìn)程和其他進(jìn)程的占用過高,這有一定概率是由于某些主線程的操作就是耗時過長捉超,或者是由于主進(jìn)程被鎖造成的胧卤。
-
除了上述的情況(1)以外,分析CPU usage之后拼岳,確定問題需要我們進(jìn)一步分析trace文件枝誊。trace文件記錄了發(fā)生ANR前后該進(jìn)程的各個線程的stack。對我們分析ANR問題最有價值的就是其中主線程的stack惜纸,一般主線程的trace可能有如下幾種情況:
(1). 主線程是running或者native而對應(yīng)的棧對應(yīng)了我們應(yīng)用中的函數(shù)叶撒,則很有可能就是執(zhí)行該函數(shù)時候發(fā)生了超時。
(2). 主線程被block:非常明顯的線程被鎖耐版,這時候可以看是被哪個線程鎖了祠够,可以考慮優(yōu)化代碼。如果是死鎖問題粪牲,就更需要及時解決了古瓤。
(3). 由于抓trace的時刻很有可能耗時操作已經(jīng)執(zhí)行完了(ANR -> 耗時操作執(zhí)行完畢 ->系統(tǒng)抓trace),這時候的trace就沒有什么用了腺阳,主線程的stack就是這樣的:
三落君、如何降低ANR的概率
有一些操作是很危險的,非常容易發(fā)生ANR亭引,在寫代碼時候一定要避免:
-
主線程讀取數(shù)據(jù):在Android中主線程去讀取數(shù)據(jù)是非常不好的绎速,Android是不允許主線程從網(wǎng)絡(luò)讀數(shù)據(jù)的,但系統(tǒng)允許主線程從數(shù)據(jù)庫或者其他地方獲取數(shù)據(jù)焙蚓,但這種操作ANR風(fēng)險很大纹冤,也會造成掉幀等洒宝,影響用戶體驗。
(1)避免在主線程query provider萌京,首先這會比較耗時待德,另外這個操作provider那一方的進(jìn)程如果掛掉了或者正在啟動,我們應(yīng)用的query就會很長時間不會返回枫夺,我們應(yīng)該在其他線程中執(zhí)行數(shù)據(jù)庫query将宪、provider的query等獲取數(shù)據(jù)的操作。
(2)sharePreference的調(diào)用:針對sharePreference的優(yōu)化點(diǎn)有很多橡庞,文章http://weishu.me/2016/10/13/sharedpreference-advices/ 詳細(xì)介紹了幾點(diǎn)sharepreference使用時候的注意事項较坛。首先sharePreference的commit()方法是同步的,apply()方法一般是異步執(zhí)行的扒最。所以在主線程不要用其commit()丑勤,用apply()替換。其次sharePreference的寫是全量寫而非增量寫吧趣,所以盡量都修改完同一apply法竞,避免改一點(diǎn)apply一次(apply()方法在Activity stop的時候主線程會等待寫入完成,提交多次就很容易卡)强挫。并且存儲文本也不宜過大岔霸,這樣會很慢。另外俯渤,如果寫入的是json或者xml呆细,由于需要加和刪轉(zhuǎn)義符號,速度會比較慢八匠。
不要在broadcastReciever的onRecieve()方法中干活絮爷,這一點(diǎn)很容易被忽略,尤其應(yīng)用在后臺的時候梨树。為避免這種情況坑夯,一種解決方案是直接開的異步線程執(zhí)行,但此時應(yīng)用可能在后臺抡四,系統(tǒng)優(yōu)先級較低柜蜈,進(jìn)程很容易被系統(tǒng)殺死,所以可以選擇開個IntentService去執(zhí)行相應(yīng)操作床嫌,即使是后臺Service也會提高進(jìn)程優(yōu)先級跨释,降低被殺可能性。
各個組件的生命周期函數(shù)都不應(yīng)該有太耗時的操作厌处,即使對于后臺Service或者ContentProvider來講,應(yīng)用在后臺運(yùn)行時候其onCreate()時候不會有用戶輸入引起事件無響應(yīng)ANR岁疼,但其執(zhí)行時間過長也會引起Service的ANR和ContentProvider的ANR阔涉。
盡量避免主線程的被鎖的情況缆娃,在一些同步的操作主線程有可能被鎖,需要等待其他線程釋放相應(yīng)鎖才能繼續(xù)執(zhí)行瑰排,這樣會有一定的ANR風(fēng)險贯要,對于這種情況有時也可以用異步線程來執(zhí)行相應(yīng)的邏輯。另外椭住, 我們要避免死鎖的發(fā)生(主線程被死鎖基本就等于要發(fā)生ANR了)崇渗。
四、造成ANR的原因及解決辦法
上面例子只是由于簡單的主線程耗時操作造成的ANR京郑,造成ANR的原因還有很多:
- 主線程阻塞或主線程數(shù)據(jù)讀取
解決辦法:避免死鎖的出現(xiàn)宅广,使用子線程來處理耗時操作或阻塞任務(wù)。盡量避免在主線程query provider些举、不要濫用SharePreferenceS
- CPU滿負(fù)荷跟狱,I/O阻塞
解決辦法:文件讀寫或數(shù)據(jù)庫操作放在子線程異步操作。
- 內(nèi)存不足
解決辦法:AndroidManifest.xml文件<applicatiion>中可以設(shè)置 android:largeHeap="true"户魏,以此增大App使用內(nèi)存驶臊。不過不建議使用此法,從根本上防止內(nèi)存泄漏叼丑,優(yōu)化內(nèi)存使用才是正道关翎。
- 各大組件ANR
各大組件生命周期中也應(yīng)避免耗時操作,注意BroadcastReciever的onRecieve()鸠信、后臺Service和ContentProvider也不要執(zhí)行太長時間的任務(wù)笤休。
五、ANR源碼分析
特別聲明:文章 理解Android ANR的觸發(fā)原理 分別記錄了由Service症副、BroadcastReceiver和ContentProvider造成的ANR店雅。下文引用該文代碼,并依據(jù)自己的簡單理解作總結(jié)贞铣。
http://www.reibang.com/p/388166988cef
六闹啦、Android ANR的信息收集
無論是四大組件或者進(jìn)程等只要發(fā)生ANR,最終都會調(diào)用AMS.appNotResponding()方法辕坝。
參考網(wǎng)址 :
Android ANR問題總結(jié)
Android ANR:原理分析及解決辦法