ANR的全稱是application not responding,根據(jù)它的意思我們就能看出來(lái)是應(yīng)用程序未響應(yīng),就像是我們?cè)陔娔X上碰到的程序未響應(yīng),一般電腦出現(xiàn)這種情況,可能是由于我們打開(kāi)了很多應(yīng)用程序,占用了大量的內(nèi)存,或者CPU時(shí)間片被一個(gè)應(yīng)用程序長(zhǎng)時(shí)間占用,不夠分配,導(dǎo)致部分應(yīng)用程序出現(xiàn)了無(wú)響應(yīng)。Android和Windows基本一樣,接下來(lái)我們就分析一下Android是怎么產(chǎn)生的這個(gè)問(wèn)題。
下圖就是我們見(jiàn)到的APP無(wú)響應(yīng)的時(shí)候出現(xiàn)的對(duì)話框,產(chǎn)生ANR的原因很多,但是只有在Activty中的Anr才會(huì)彈出對(duì)話框,對(duì)話框有兩個(gè)選項(xiàng),一個(gè)是等待,一個(gè)是關(guān)閉程序供用戶選擇:
一、為什么會(huì)產(chǎn)生ANR(Application not response)這種情況呢?
在Android里, App的響應(yīng)能力是由Activity Manager和Window Manager系統(tǒng)服務(wù)來(lái)監(jiān)控的.,通常情況下產(chǎn)生ANR有這么三個(gè)條件:
1.只有主線程才會(huì)產(chǎn)生ANR,主線程就是UI線程;
2.必須發(fā)生某些輸入事件或特定操作,比如按鍵或觸屏等輸入事件,在BroadcastReceiver或Service的各個(gè)生命周期調(diào)用函數(shù);
3.上述事件響應(yīng)超時(shí),不同的context規(guī)定的上限時(shí)間不同
具備了以上三個(gè)條件,那么加上這兩個(gè)環(huán)境,就會(huì)產(chǎn)生ANR了:
1.5秒內(nèi)無(wú)法對(duì)輸入事件(按鍵及觸摸)做出響應(yīng)
2.廣播接收器無(wú)法在10秒內(nèi)結(jié)束運(yùn)行
二、ANR時(shí)系統(tǒng)做了什么
1.彈出一個(gè)對(duì)話框
2.將ANR信息輸出到traces.txt文件中
traces.txt文件是一個(gè)ANR記錄文件,用于開(kāi)發(fā)人員調(diào)試,目錄位于/data/anr中,無(wú)需root權(quán)限即可通過(guò)pull命令獲取,下面的命令可以將traces.txt文件拷貝到當(dāng)前目錄下
adb pull /data/anr
3.將ANR信息輸出到Logcat中
三、對(duì)ANR產(chǎn)生詳細(xì)分析
為什么會(huì)產(chǎn)生ANR,在第一條講解中,具體剖析(部分文章參考簡(jiǎn)書(shū)作者):
a.主線程對(duì)輸入事件5秒內(nèi)沒(méi)有處理完畢
b.主線程在執(zhí)行BroadcastReceiver的onReceive()函數(shù)時(shí)10秒內(nèi)沒(méi)有處理完畢
c.主線程在Service的各個(gè)生命周期函數(shù)時(shí)20秒內(nèi)沒(méi)有處理完畢。
那么導(dǎo)致ANR的根本原因是什么呢?簡(jiǎn)單的總結(jié)有以下兩點(diǎn):
1.主線程執(zhí)行了耗時(shí)操作,比如數(shù)據(jù)庫(kù)操作或網(wǎng)絡(luò)編程
2.其他進(jìn)程(就是其他程序)占用CPU導(dǎo)致本進(jìn)程得不到CPU時(shí)間片,比如其他進(jìn)程的頻繁讀寫操作可能會(huì)導(dǎo)致這個(gè)問(wèn)題。
細(xì)分的話,導(dǎo)致ANR的原因有如下幾點(diǎn):
1.耗時(shí)的網(wǎng)絡(luò)訪問(wèn)
2.大量的數(shù)據(jù)讀寫
3.數(shù)據(jù)庫(kù)操作
4.硬件操作(比如camera)
5.調(diào)用thread的join()方法、sleep()方法、wait()方法或者等待線程鎖的時(shí)候
6.service binder的數(shù)量達(dá)到上限
7.system server中發(fā)生WatchDog ANR
8.service忙導(dǎo)致超時(shí)無(wú)響應(yīng)
9.其他線程持有鎖,導(dǎo)致主線程等待超時(shí)
10.其它線程終止或崩潰導(dǎo)致主線程一直等待。
四、ANR機(jī)制的實(shí)現(xiàn)原理:
文章:http://gityuan.com/2016/07/02/android-anr/從源碼角度詳細(xì)的分析了ANR機(jī)制實(shí)現(xiàn)的原理。對(duì)于上一章講到的1-4中情況,分別找到了其源碼中是如何實(shí)現(xiàn)的,對(duì)于每一種大概原理如下:1.在進(jìn)行相關(guān)操作調(diào)用hander.sendMessageAtTime()發(fā)送一個(gè)ANR的消息,延時(shí)時(shí)間為ANR發(fā)生的時(shí)間(如前臺(tái)Service是當(dāng)前時(shí)間20s之后)。2.進(jìn)行相關(guān)的操作3.操作結(jié)束后向remove掉該條message。如果相關(guān)的操作在規(guī)定時(shí)間沒(méi)有執(zhí)行完成,該條message將被handler取出并執(zhí)行,就發(fā)生了ANR。
下面以BroadcastReceiver為例詳細(xì)介紹:
BroadcastQueue.processNextBroadcast()
final void processNextBroadcast(boolean fromMsg) { … synchronized (mService) { … do { if (mOrderedBroadcasts.size() == 0) { … if (mService.mProcessesReady && r.dispatchTime > 0) { long now = SystemClock.uptimeMillis(); if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mTimeoutPeriod * numReceivers))) { //1.發(fā)送延時(shí)消息 broadcastTimeoutLocked(false); // forcibly finish this broadcast forceReceive = true; r.state = BroadcastRecord.IDLE; } } if (r.state != BroadcastRecord.IDLE) { if (DEBUG_BROADCAST) Slog.d(TAG, “processNextBroadcast(” + mQueueName + “) called when not idle (state=” + r.state + “)”); return; } if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { // No more receivers for this broadcast! Send the final // result if requested… if (r.resultTo != null) { try { //2. 處理廣播消息 performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId); // Set this to null so that the reference // (local and remote) isn’t kept in the mBroadcastHistory. r.resultTo = null; } catch (RemoteException e) { … } } //3.取消延時(shí)消息 cancelBroadcastTimeoutLocked(); … } } while (r == null) ; … } } }
1.發(fā)送延時(shí)消息:broadcastTimeoutLocked(false): final void broadcastTimeoutLocked(boolean fromMsg) { … long now = SystemClock.uptimeMillis(); if (fromMsg) { if (mService.mDidDexOpt) { // Delay timeouts until dexopt finishes. mService.mDidDexOpt = false; long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod; setBroadcastTimeoutLocked(timeoutTime); return; } if (!mService.mProcessesReady) { return; } long timeoutTime = r.receiverTime + mTimeoutPeriod; if (timeoutTime > now) { setBroadcastTimeoutLocked(timeoutTime); return; } }
他調(diào)用了setBroadcastTimeoutLocked(long timeoutTime): final void setBroadcastTimeoutLocked(long timeoutTime) { if (! mPendingBroadcastTimeoutMessage) { Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); mHandler.sendMessageAtTime(msg, timeoutTime); mPendingBroadcastTimeoutMessage = true; } }
傳入setBroadcastTimeoutLocked(long timeoutTime)的時(shí)間xxx + mTimeoutPeriod,mTimeoutPeriod就是onRecieve()可以執(zhí)行的時(shí)間,在BroadcastQueue初始化時(shí)候被賦值,前臺(tái)隊(duì)列為10s后臺(tái)隊(duì)列為60s:
ActivityManagerService.java: public ActivityManagerService(Context systemContext) { … static final int BROADCAST_FG_TIMEOUT = 10 * 1000; static final int BROADCAST_BG_TIMEOUT = 60 * 1000; … mFgBroadcastQueue = new BroadcastQueue(this, mHandler, “foreground”, BROADCAST_FG_TIMEOUT, false); mBgBroadcastQueue = new BroadcastQueue(this, mHandler, “background”, BROADCAST_BG_TIMEOUT, true); … }
performReceiveLocked()為廣播的實(shí)際處理,就不展開(kāi)了
cancelBroadcastTimeoutLocked() :
該方法的主要工作是當(dāng)service啟動(dòng)完成,則移除服務(wù)超時(shí)消息SERVICE_TIMEOUT_MSG。
final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}
五、如何避免ANR呢?
1.避免在主線程執(zhí)行耗時(shí)操作,所有耗時(shí)操作應(yīng)新開(kāi)一個(gè)子線程完成,然后再在主線程更新UI。
2.BroadcastReceiver要執(zhí)行耗時(shí)操作時(shí)應(yīng)啟動(dòng)一個(gè)service,將耗時(shí)操作交給service來(lái)完成。
3.避免在Intent Receiver里啟動(dòng)一個(gè)Activity,因?yàn)樗鼤?huì)創(chuàng)建一個(gè)新的畫面,并從當(dāng)前用戶正在運(yùn)行的程序上搶奪焦點(diǎn)。如果你的應(yīng)用程序在響應(yīng)Intent廣 播時(shí)需要向用戶展示什么,你應(yīng)該使用Notification Manager來(lái)實(shí)現(xiàn)。
UI線程盡量只做跟UI相關(guān)的工作
耗時(shí)的工作()比如數(shù)據(jù)庫(kù)操作,I/O,網(wǎng)絡(luò)操作),采用單獨(dú)的工作線程處理
用Handler來(lái)處理UIthread和工作thread的交互
UI線程,例如:
Activity:onCreate(), onResume(), onDestroy(), onKeyDown(), onClick(),etc
AsyncTask: onPreExecute(), onProgressUpdate(), onPostExecute(), onCancel,etc
Mainthread handler: handleMessage(), post*(runnable r), etc
…
ANR分析:需要關(guān)注CPU/IO,trace死鎖等數(shù)據(jù)。
六、總結(jié)
ANR在我們開(kāi)發(fā)APP中也比較常見(jiàn),總的來(lái)說(shuō),我們需要執(zhí)行耗時(shí)得操作就在子線程中執(zhí)行,避免在主線程中執(zhí)行。
參考鏈接
https://blog.csdn.net/jaychou_maple/article/details/78782822