天天看點

Android ANR機制的原理以及問題分析(三)

文章目錄

        • 一、前言
        • 二、Broadcast Timeout
        • 三、Broadcast 設定 定時器
        • 四、Broadcast 重置 定時器
        • 五、Broadcast 觸發ANR

一、前言

上一篇我們對Service類型的ANR做了介紹,本篇我們将集合源碼,對四種ANR類型中的Broadcast Timeout類型的觸發機制做詳盡的介紹。

二、Broadcast Timeout

在ActivityManagerService 中,構造了兩種Broadcast timeout,分别是 前台FG 10s和

背景BG 60s.

mFgBroadcastQueue/mBgBroadcastQueue ⽤來表示foreground和background

⼴播隊列.

⾸先看下如下⼏個資料的定義:

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
// How long we allow a receiver to run before giving up on it.
 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);
           
//frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
/**
 * Lists of all active broadcasts that are to be executed
immediately
 * (without waiting for another broadcast to finish). 
Currently this only
 * contains broadcasts to registered receivers, to avoid
spinning up
 * a bunch of processes to execute IntentReceiver
components. Background-
 * and foreground-priority broadcasts are queued
separately.
 */
 /**
 * 并⾏⼴播隊列,可以⽴刻執⾏,⽽⽆需等待另⼀個⼴播運⾏完成,該隊列隻允
許動态已注冊的⼴播,
 * 從⽽避免發⽣同時拉起⼤量程序來執⾏⼴播,前台的和背景的⼴播分别位于獨
⽴的隊列。
 */
 final ArrayList<BroadcastRecord> mParallelBroadcasts = new
ArrayList<>();
 /**
 * List of all active broadcasts that are to be executed
one at a time.
 * The object at the top of the list is the currently
activity broadcasts;
 * those after it are waiting for the top to finish. As
with parallel
 * broadcasts, separate background- and foreground-priority
queues are
 * maintained.
 */
 /**
 *有序⼴播隊列,同⼀時間隻允許執⾏⼀個⼴播,該隊列頂部的⼴播便是活動⼴
播,
 *其他⼴播必須等待該⼴播結束才能運⾏,和并⾏⼴播隊列⼀樣也是獨⽴差別前
台的和背景的⼴播。
 */
 final ArrayList<BroadcastRecord> mOrderedBroadcasts = new
ArrayList<>();
           

三、Broadcast 設定 定時器

調⽤ processNextBroadcast來處理⼴播.其流程為先處理并⾏⼴播,再處理目前有序

⼴播,最後擷取并處理下條有序⼴播.

//frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
BroadcastQueue(ActivityManagerService service, Handler handler,
 		String name, long timeoutPeriod, boolean
		allowDelayBehindServices) {
	 mService = service;
	 mHandler = new BroadcastHandler(handler.getLooper());
	 mQueueName = name;
	 mTimeoutPeriod = timeoutPeriod;
	 mDelayBehindServices = allowDelayBehindServices; 
} 
           

processNextBroadcast 最終會調⽤processNextBroadcastLocked (fromMsg

=false).

此處mService為ActivityManagerService,整個流程還是⽐較⻓的,全程持有AMS

鎖,這個⽅法⽐較⻓,我省略的部分不在這⾥重點關注的部分.是以⼴播的隊列很⻓的

話,直接會嚴重影響這個⼿機的性能與流暢度,這⾥可以做⼀個監控,看看系統處理⼴

播都需要hold 多久的ams lock.

整個processNextBroadcast的流程⼤緻分為下⾯⼏個步驟:

  1. 設定⼴播逾時延時消息,設定定時器: setBroadcastTimeoutLocked:
  2. 處理并⾏⼴播 mParallelBroadcasts 執⾏

    deliverToRegisteredReceiverLocked

  3. 處理有序⼴播mOrderedBroadcasts當⼴播接收者等待時間過⻓, now > now >

    r.dispatchTime + (2mTimeoutPeriodnumReceivers) r.dispatchTime + (2mTimeoutPeriodnumReceivers) 則調⽤

    broadcastTimeoutLocked(false) 則強制結束這條⼴播;

final void processNextBroadcast(boolean fromMsg) {
 	synchronized (mService) {
 		processNextBroadcastLocked(fromMsg, false);
 	}
 }
 final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
 	BroadcastRecord r;
 	//...省略
 	//【1】處理并⾏⼴播 mParallelBroadcasts
 	while (mParallelBroadcasts.size() > 0) {
		 r = mParallelBroadcasts.remove(0);
		 r.dispatchTime = SystemClock.uptimeMillis();
		 r.dispatchClockTime = System.currentTimeMillis();
		 final int N = r.receivers.size();
		 for (int i=0; i<N; i++) {
			 Object target = r.receivers.get(i);
			 //分發⼴播給已注冊的receiver
			 deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
		 }
		 //添加⼴播曆史統計
		 addBroadcastToHistoryLocked(r);
	}
	 // Now take care of the next serialized one...
	 // mPendingBroadcast正常情況下是空的,如果不是空,說明目前有需
	要處理的⼴播,⼀般隻有在
	 // 等待⽬标程序啟動來處理對應的⼴播的情況下會出現,這個情況下就僅
	僅檢查程序是否存在.
	 if (mPendingBroadcast != null) {
		 boolean isDead;
		 if (mPendingBroadcast.curApp.pid > 0) {
			 synchronized (mService.mPidsSelfLocked) {
				 ProcessRecord proc =
				 mService.mPidsSelfLocked.get(
				 mPendingBroadcast.curApp.pid);
				 isDead = proc == null || proc.crashing;
	 		}
		 } else {
 			final ProcessRecord proc =
			mService.mProcessNames.get(mPendingBroadcast.curApp.processName,
					mPendingBroadcast.curApp.uid);
 			isDead = proc == null || !proc.pendingStart;
 		}
 		if (!isDead) {
	 		// It's still alive, so keep waiting
	 		// 程序還存在,沒有死亡,繼續等待.
 			return;
 		} else {
 			//程序已經死亡,清空mPendingBroadcast,忽略目前⼴播.
 			mPendingBroadcast.state =
			BroadcastRecord.IDLE;
 			mPendingBroadcast.nextReceiver =
			mPendingBroadcastRecvIndex;
 			mPendingBroadcast = null;
 		}
 }
 boolean looped = false;
 //【2】 處理有序⼴播mOrderedBroadcasts
 do {
	 if (mOrderedBroadcasts.size() == 0) {
		 // No more broadcasts pending, so all done!
		 //mOrderedBroadcasts 有序⼴播隊列沒有需要處理的⼴播,直接傳回.
		 mService.scheduleAppGcsLocked();
		 if (looped) {
			 // If we had finished the last ordered broadcast, then
			 // make sure all processes have correct oom and sched
			 // adjustments.
			 mService.updateOomAdjLocked();
		 }
 		return;
 }
 //擷取有序⼴播的第⼀個⼴播
 r = mOrderedBroadcasts.get(0);
 boolean forceReceive = false;
 // Ensure that even if something goes awry with the timeout
 // detection, we catch "hung" broadcasts here, discard them,
 // and continue to make progress.
 // This is only done if the system is ready so that PRE_BOOT_COMPLETED
 // receivers don't get executed with timeouts. They're intended for
 // one time heavy lifting after system upgrades and can take
 // significant amounts of time.
 int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
 if (mService.mProcessesReady && r.dispatchTime > 0) {
 	long now = SystemClock.uptimeMillis();
 	if ((numReceivers > 0) && (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
	 	//...
	 	//當⼴播處理時間逾時,則強制結束這條⼴播
	 	broadcastTimeoutLocked(false); // forcibly finish this broadcast
	 	forceReceive = true;
	 	r.state = BroadcastRecord.IDLE;
 	}
 }
 //...省略
 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) {
		 //處理⼴播消息消息,調⽤到onReceive()
		 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;
 	 }
 	if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
 	//【3】取消BROADCAST_TIMEOUT_MSG消息
 	cancelBroadcastTimeoutLocked();
 	// ... and on to the next...
 	addBroadcastToHistoryLocked(r);
 	//...省略
 }
 mOrderedBroadcasts.remove(0);
 r = null;
 looped = true;
 continue;
 }
 } while (r == null);
 	// Get the next receiver...
 	int recIdx = r.nextReceiver++;
 	// Keep track of when this receiver started, and make sure there
 	// is a timeout message pending to kill it if need be.
 	r.receiverTime = SystemClock.uptimeMillis();
 	if (recIdx == 0) {
 		r.dispatchTime = r.receiverTime;
 		r.dispatchClockTime = System.currentTimeMillis();
 		//...
 	}
 	if (! mPendingBroadcastTimeoutMessage) {
 		long timeoutTime = r.receiverTime + mTimeoutPeriod;
 		//【4】設定Broadcast timeout
 		setBroadcastTimeoutLocked(timeoutTime);
 	}
 	final BroadcastOptions brOptions = r.options;
 	final Object nextReceiver = r.receivers.get(recIdx);
 	if (nextReceiver instanceof BroadcastFilter) {
 		// Simple case: this is a registered receiver who gets a direct call.
 		BroadcastFilter filter = (BroadcastFilter)nextReceiver;
 
 		deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
 			if (r.receiver == null || !r.ordered) {
 				// The receiver has already finished, so schedule to process the next one.
 				r.state = BroadcastRecord.IDLE;
 				scheduleBroadcastsLocked();
 			} else {
 				if (brOptions != null &&
						brOptions.getTemporaryAppWhitelistDuration() > 0) {
 
					scheduleTempWhitelistLocked(filter.owningUid,
 
					brOptions.getTemporaryAppWhitelistDuration(), r);
 				}
 			}
 			return;
 	}
 	//...省略..各種權限檢查,⼴播合法性檢查
 
 	// Is this receiver's application already running?
 	if (app != null && app.thread != null && !app.killed) {
 	try {
 		app.addPackage(info.activityInfo.packageName,
 
		info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
 		processCurBroadcastLocked(r, app, skipOomAdj);
 		return;
 	} catch (RemoteException e) {
		Slog.w(TAG, "Exception when sending broadcast to " + r.curComponent, e);
 	} catch (RuntimeException e) {
 		Slog.wtf(TAG, "Failed sending broadcast to " + r.curComponent + " with " + r.intent, e);
 		// If some unexpected exception happened, just skip
 		// this broadcast. At this point we are not in the call
 		// from a client, so throwing an exception out from here
 		// will crash the entire system instead of just whoever
 		// sent the broadcast.
 		logBroadcastReceiverDiscardLocked(r);
 		finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);
 		scheduleBroadcastsLocked();
 		// We need to reset the state if we failed to start the receiver.
 		r.state = BroadcastRecord.IDLE;
 		return;
 	}
.
 }
 //startProcessLocked 啟動待執⾏的APP程序
 if((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true,
 		r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent,
		(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
 		== null) {
 	// Ah, this recipient is unavailable. Finish it
	if necessary,
 	// and mark the broadcast record as ready for the next.
 	Slog.w(TAG, "Unable to launch app " + info.activityInfo.applicationInfo.packageName + "/"
 		+ receiverUid + " for broadcast "
 		+ r.intent + ": process is bad");
 	logBroadcastReceiverDiscardLocked(r);
 	finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false);
 	scheduleBroadcastsLocked();
 	r.state = BroadcastRecord.IDLE;
 	return;
 }
}
           

四、Broadcast 重置 定時器

上⾯代碼中 113⾏

//【3】取消BROADCAST_TIMEOUT_MSG消息
 cancelBroadcastTimeoutLocked();
 //....
final void cancelBroadcastTimeoutLocked() {
 	if (mPendingBroadcastTimeoutMessage) {
 		mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
 		mPendingBroadcastTimeoutMessage = false;
 	}
}
           

處理⼴播消息消息,調⽤應⽤⾃⼰的onReceive(),執⾏成功則調⽤

cancelBroadcastTimeoutLocked移除BROADCAST_TIMEOUT_MSG消息

五、Broadcast 觸發ANR

private final class BroadcastHandler extends Handler {
 	public BroadcastHandler(Looper looper) {
 		super(looper, null, true);
 	}
 	@Override
 	public void handleMessage(Message msg) {
 		switch (msg.what) {
 			case BROADCAST_TIMEOUT_MSG: {
 				synchronized (mService) {
 					broadcastTimeoutLocked(true);
 				}
 			} break;
 		}
 	}
}
//有上⾯可知,fromMsg = true
final void broadcastTimeoutLocked(boolean fromMsg) {
 	if (fromMsg) {
 		mPendingBroadcastTimeoutMessage = false;
 	}
 	if (mOrderedBroadcasts.size() == 0) {
 		return;
 	}
 	long now = SystemClock.uptimeMillis();
 	BroadcastRecord r = mOrderedBroadcasts.get(0);
 	if (fromMsg) {
 		if (!mService.mProcessesReady) {
 			// Only process broadcast timeouts if the system is ready. That way
 			// PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
 			// to do heavy lifting for system up.
 			//隻有當system ready時,才會觸發broadcast Anr
 			return;
 		}
 		long timeoutTime = r.receiverTime + mTimeoutPeriod;
 		if (timeoutTime > now) {
 			// We can observe premature timeouts because we do not cancel and reset the
			// broadcast timeout message after each receiver finishes. Instead, we set up
			// an initial timeout then kick it down the road a little
			// further as needed when it expires.
			//如果目前正在執⾏的receiver沒有逾時,則重新設定⼴播逾時
 			if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Premature timeout ["
 				+ mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
 				+ timeoutTime);
 			setBroadcastTimeoutLocked(timeoutTime);
 			return;
 		}
 	}
 	BroadcastRecord br = mOrderedBroadcasts.get(0);
 	if (br.state == BroadcastRecord.WAITING_SERVICES) {
 		// In this case the broadcast had already finished, but we had decided to wait
 		// for started services to finish as well before
		// going on. So if we have actually
 		// waited long enough time timeout the broadcast, let's give up on the whole thing
 		// and just move on to the next.
 		//⼴播已經處理完成,但需要等待已啟動service執⾏完成。當等待⾜夠時間,則處理下⼀條⼴播。
 		Slog.i(TAG, "Waited long enough for: " + (br.curComponent != null
 				? br.curComponent.flattenToShortString() : "(null)"));
 		br.curComponent = null;
 		br.state = BroadcastRecord.IDLE;
 		processNextBroadcast(false);
 		return;
 	}
 	// If the receiver app is being debugged we quietly ignore unresponsiveness, just
 	// tidying up and moving on to the next broadcast without crashing or ANRing this
 	// app just because it's stopped at a breakpoint.
 	final boolean debugging = (r.curApp != null && r.curApp.debugging);
 	Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
 		+ ", started " + (now - r.receiverTime) + "ms ago");
 	r.receiverTime = now;
 	if (!debugging) {
 		r.anrCount++;
 	}
 	ProcessRecord app = null;
 	String anrMessage = null;
 	Object curReceiver;
 	if (r.nextReceiver > 0) {
 		curReceiver = r.receivers.get(r.nextReceiver-1);
 		r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
 	} else {
 		curReceiver = r.curReceiver;
 	}
 	Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
 	logBroadcastReceiverDiscardLocked(r);
 	if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
 		BroadcastFilter bf = (BroadcastFilter)curReceiver;
 		if (bf.receiverList.pid != 0 && bf.receiverList.pid != ActivityManagerService.MY_PID) {
 			synchronized (mService.mPidsSelfLocked) {
 				app = mService.mPidsSelfLocked.get(bf.receiverList.pid);
			}
 		}
 	} else {
 		app = r.curApp;
 	}
 	if (app != null) {
 		anrMessage = "Broadcast of " + r.intent.toString();
 	}
 	if (mPendingBroadcast == r) {
 		mPendingBroadcast = null;
 	}
 	// Move on to the next receiver.
 	finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false);
 	scheduleBroadcastsLocked();
 	if (!debugging && anrMessage != null) {
 		// Post the ANR to the handler since we do not want to process ANRs while
 		// potentially holding our lock.
 		mHandler.post(new AppNotResponding(app, anrMessage));
 	}
}
           

最終調⽤AppErrors中的appNotResponding,顯示ANR對話框

private final class AppNotResponding implements Runnable {
	 private final ProcessRecord mApp;
	 private final String mAnnotation;
	 public AppNotResponding(ProcessRecord app, String annotation) {
		 mApp = app;
		 mAnnotation = annotation;
	 }
	 @Override
	 public void run() {
	 	mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
	 }
}