ActivityManagerService廣播并行發(fā)送與串行發(fā)送示例解析
"并行"廣播的發(fā)送
本文以 ActivityManagerService之廣播(1): 注冊與發(fā)送 為基礎(chǔ),分析“串行”和“并行”廣播的發(fā)送流程,并介紹廣播 ANR 的原理。
// 1. 獲取廣播隊列
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// 2. 創(chuàng)建廣播記錄
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers,
resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, backgroundActivityStartsToken,
timeoutExempt);
// 3. 廣播記錄加入到并行隊列中
queue.enqueueParallelBroadcastLocked(r);
// 4. 調(diào)度發(fā)送廣播
queue.scheduleBroadcastsLocked();
第3步,把廣播記錄保存到并行隊列中
// BroadcastQueue.java
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
// mParallelBroadcasts 類型為 ArrayList<BroadcastRecord>
mParallelBroadcasts.add(r);
enqueueBroadcastHelper(r);
}
第4步,調(diào)度發(fā)送廣播,最終會調(diào)用如下方法
// BroadcastQueue.java
// 此時,參數(shù) fromMsg 為 true,skipOomAdj 為 false
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// 遍歷"并行"廣播隊列
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);
// 廣播發(fā)送給動態(tài)接收器
deliverToRegisteredReceiverLocked(r,
(BroadcastFilter) target, false, i);
}
addBroadcastToHistoryLocked(r);
}
// ... 省略"串行"廣播的發(fā)送 ...
}
雖然名為“并行”廣播,但是仍然是從隊列取出廣播,然后逐個發(fā)送給動態(tài)接收器。很顯然,這里的行為與“并行”的含義并不一致?那么廣播的“并行”發(fā)送究竟是什么意思?
接著看“并行”廣播如何發(fā)送給動態(tài)接收器的
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
// ... 省略一大堆的權(quán)限或者異常檢測 ...
// 一個廣播可能有多個接收者,因此需要一個數(shù)組來保存發(fā)送的狀態(tài)
r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
// ordered 目前為 false
if (ordered) {
// ...
}
} else if (filter.receiverList.app != null) {
// 馬上要發(fā)送廣播給接收方,因此要暫時解凍接收方的進程
mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app);
}
try {
if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
// ... 處于備份狀態(tài)中 ...
} else {
r.receiverTime = SystemClock.uptimeMillis();
// 保存允許從后臺啟動activity的token
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
// 添加到省電模式白名單中
maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
// 執(zhí)行廣播的發(fā)送
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
if (filter.receiverList.app != null
&& r.allowBackgroundActivityStarts && !r.ordered) {
postActivityStartTokenRemoval(filter.receiverList.app, r);
}
}
// ordered 目前為 false
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
} catch (RemoteException e) {
// ...
}
}
拋開一些細節(jié),直接看 performReceiveLocked()
// BroadcastQueue.java
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
// 動態(tài)廣播接收器的進程,應(yīng)該是存在的
if (app != null) {
final IApplicationThread thread = app.getThread();
if (thread != null) {
try {
// 發(fā)送廣播給接收方進程
thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser,
} catch (RemoteException ex) {
// ...
}
} else {
throw new RemoteException("app.thread must not be null");
}
} else {
// ...
}
}
很簡單,就是通過進程 attach 的 IApplicationThread 接口,發(fā)送廣播給進程。這個過程,暫時先不分析,后面會分析到。
那么,現(xiàn)在來回答一下,何為“并行”廣播?其實這個答案,我也是對比了串行廣播的發(fā)送過程,才得出來的。所謂的"并行"發(fā)送,實際上就是把廣播逐個發(fā)送給動態(tài)接收器,但是不需要等待前一個接收器反饋處理結(jié)果,就可以發(fā)送下一個。而“串行”廣播的發(fā)送,是需要等待前一個廣播接收器反饋處理結(jié)果后,才能調(diào)度發(fā)送下一個廣播。
“串行”廣播的發(fā)送
// 1.獲取廣播隊列
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 2.創(chuàng)建廣播記錄
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
receivers, resultTo, resultCode, resultData, resultExtras,
ordered, sticky, false, userId, allowBackgroundActivityStarts,
backgroundActivityStartsToken, timeoutExempt);
// 3.廣播記錄加入到串行隊列中
queue.enqueueOrderedBroadcastLocked(r);
// 4.調(diào)度發(fā)送廣播
queue.scheduleBroadcastsLocked();
第3步,廣播加入到串行隊列中
// BroadcastQueue.java
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}
// BroadcastDispatcher.java
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mOrderedBroadcasts.add(r);
}
并行發(fā)送的廣播保存到 BroadcastQueue#mParallelBroadcasts 中,而串行發(fā)送的廣播保存到 BroadcastDispatcher#mOrderedBroadcasts 中,為何要這樣設(shè)計呢?有興趣的讀者可以研究下。
第4步,“串行”廣播的調(diào)度發(fā)送,仍然使用的是 processNextBroadcastLocked() 方法,但是代碼量是非常的大,下面將把函數(shù)分段解析。
processNextBroadcastLocked() 函數(shù)有400多行代碼,這個函數(shù)里有很多東西都可以抽出來的,但是隨著版本的更新,這塊代碼一直沒有優(yōu)化過。
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// 檢測 receiver 進程是否死亡
boolean isDead;
if (mPendingBroadcast.curApp.getPid() > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
mPendingBroadcast.curApp.getPid());
isDead = proc == null || proc.mErrorState.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.isPendingStart();
}
if (!isDead) {
// 進程仍然存活,結(jié)束此次廣播的處理流程,繼續(xù)等待
// 等待什么呢?等待廣播進程起來,并與 AMS 完成 attach application
// 在 attach application 的過程中,會完成廣播的發(fā)送
return;
} else {
// 進程死亡,繼續(xù)處理下一個廣播
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
當(dāng)發(fā)送一個廣播給 receiver 時,如果 receiver 進程沒有啟動,那么會先 fork 一個 receiver 進程,然后用 mPendingBroadcast 保存待發(fā)送的廣播。當(dāng) receiver 進程起來的時候,會與 AMS 執(zhí)行 attach application 過程,在這個過程中,會自動把 mPendingBroadcast 保存的廣播發(fā)送給 receiver 進程。
因此,這里檢測到 mPendingBroadcast 不為 null 時,那么 receiver 進程肯定在啟動中,只要 receiver 進程沒有死亡,就什么也不用做,因為廣播會自動發(fā)送給 receiver 進程。
接著看下一步的處理
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
// ...
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// ...
}
boolean looped = false;
// 2. 通過 do-while 循環(huán),找到一個現(xiàn)在可以處理的廣播
do {
final long now = SystemClock.uptimeMillis();
// 獲取一個待處理的廣播
r = mDispatcher.getNextBroadcastLocked(now);
if (r == null) {
// ... 沒有廣播需要處理 ...
return;
}
boolean forceReceive = false;
// 處理嚴重超時的廣播,有兩種情況
// 一種情況是,在系統(tǒng)還沒有起來前,發(fā)送的廣播得不到執(zhí)行,發(fā)生嚴重超時
// 另外一種情況是,在系統(tǒng)起來后,有一些超時豁免的廣播,發(fā)生了嚴重超時
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
if (r.state != BroadcastRecord.IDLE) {
return;
}
// 當(dāng)前廣播因為某種原因,終止處理,然后處理下一個廣播
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// ...
// 通知 BroadcastDispatcher ,不處理這個廣播了
mDispatcher.retireBroadcastLocked(r);
r = null;
looped = true;
// 下一次循環(huán),獲取下一個廣播來處理
continue;
}
// 處理推遲發(fā)送廣播的情況
if (!r.deferred) {
final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
if (mDispatcher.isDeferringLocked(receiverUid)) {
// ...
// 保存推遲發(fā)送的廣播
mDispatcher.addDeferredBroadcast(receiverUid, defer);
r = null;
looped = true;
// 下一次循環(huán)時,獲取下一個廣播來處理
continue;
}
}
} while (r == null);
先從整體看,通過一個 do-while 循環(huán),最終是為了找到下一個處理的廣播。為何要用一個循環(huán)來尋找呢? 因為廣播可能沒有接收器,或者已經(jīng)嚴重超時,又或者廣播需要推遲發(fā)送。所以要通過一個循環(huán),找到一個能立即發(fā)送的廣播。
由于本文主要是為了分析廣播發(fā)送的整體流程,對于有些細節(jié),只做注釋而不做細致分析。需要深入研究的讀者,可以在本文的基礎(chǔ)上繼續(xù)分析。
繼續(xù)接著看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// ...
}
boolean looped = false;
// 2. 通過 do-while 循環(huán),找到一個現(xiàn)在可以處理的廣播
do {
final long now = SystemClock.uptimeMillis();
// 獲取一個待處理的廣播
r = mDispatcher.getNextBroadcastLocked(now);
// ...
} while (r == null);
// 走到這里,表示已經(jīng)獲取了一個現(xiàn)在可以處理的廣播
int recIdx = r.nextReceiver++;
// 3. 在發(fā)送廣播之前,先發(fā)送一個超時消息
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
// 在廣播開始發(fā)送給第一個接收器時,記錄發(fā)送的時間
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
}
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
setBroadcastTimeoutLocked(timeoutTime);
}
在廣播發(fā)送給一個 receiver 之前,會先發(fā)送一個超時消息。從廣播準(zhǔn)備發(fā)送給一個 receiver 算起,到 receiver 處理完廣播,并反饋給 AMS,如果這個時間段超過了一個時間閾值,就會引發(fā) ANR。觸發(fā) ANR 的代碼設(shè)計非常巧妙,后面會具體分析這個過程。
接著看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
// ...
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// ...
}
boolean looped = false;
// 2. 通過 do-while 循環(huán),找到一個現(xiàn)在可以處理的廣播
do {
// ...
} while (r == null);
int recIdx = r.nextReceiver++;
// ...
// 3. 在發(fā)送廣播之前,先發(fā)送一個超時消息
// 當(dāng)廣播處理超時時,會觸發(fā) ANR
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
setBroadcastTimeoutLocked(timeoutTime);
}
final BroadcastOptions brOptions = r.options;
// 4. 獲取一個 receiver
final Object nextReceiver = r.receivers.get(recIdx);
// 5. 如果這個接收器是動態(tài)接收器,先把廣播發(fā)送給它
// 注意,這里處理的是有序廣播發(fā)送給動態(tài)接收器的情況
if (nextReceiver instanceof BroadcastFilter) {
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
// 發(fā)送廣播給動態(tài)接收器
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ mQueueName + "]: ordered="
+ r.ordered + " receiver=" + r.receiver);
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {
if (filter.receiverList != null) {
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
}
}
// 注意,把廣播發(fā)送給 動態(tài)receiver 后,直接返回
return;
}
現(xiàn)在一切就緒,那么開始獲取一個 receiver,當(dāng)這個 receiver 是一個動態(tài)接收器時,直接發(fā)送廣播給它,這個發(fā)送過程前面已經(jīng)分析過。
注意,這里處理的情況是,把有序廣播發(fā)送給動態(tài)接收器。并且發(fā)送完成后,直接 return, 也就是結(jié)束了此次廣播的發(fā)送流程。
一個廣播可能有多個接收器,為何這里只發(fā)送給一個動態(tài)接收器,就直接返回了? 這就是從“串行”廣播的本質(zhì),需要等待當(dāng)前的廣播接收器處理完廣播,并返回結(jié)果后,才能把廣播發(fā)送給下一個廣播接收器。
接著看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
// ...
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// ...
}
boolean looped = false;
// 2. 通過 do-while 循環(huán),找到一個現(xiàn)在可以處理的廣播
do {
final long now = SystemClock.uptimeMillis();
// 獲取一個待處理的廣播
r = mDispatcher.getNextBroadcastLocked(now);
// ...
} while (r == null);
// 走到這里,表示已經(jīng)獲取了一個現(xiàn)在可以處理的廣播
int recIdx = r.nextReceiver++;
// 3. 在發(fā)送廣播之前,先發(fā)送一個超時消息
r.receiverTime = SystemClock.uptimeMillis();
// ...
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
setBroadcastTimeoutLocked(timeoutTime);
}
final BroadcastOptions brOptions = r.options;
// 4. 獲取一個 receiver
final Object nextReceiver = r.receivers.get(recIdx);
// 5. 如果這個接收器是動態(tài)接收器,先把廣播發(fā)送給它
// 注意,這里處理的是有序廣播發(fā)送給動態(tài)接收器的情況
if (nextReceiver instanceof BroadcastFilter) {
// ...
return;
}
// 走到這里,表示當(dāng)前的廣播接收器,是靜態(tài)接收器
// 獲取靜態(tài)接收器的信息
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
boolean skip = false;
// 6. 檢測是否不需要把廣播發(fā)送給靜態(tài)接收器
// ... 省略一大堆的檢測代碼 ...
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid);
if (!skip) {
// 檢測是否允許把廣播發(fā)送給靜態(tài)接收器
final int allowed = mService.getAppStartModeLOSP(
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
// 例如,大于等于 O+ 版本的 app ,不允許廣播發(fā)送給靜態(tài)接收器
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// ephemeral app 會返回這個模式
if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
skip = true;
} else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|| (r.intent.getComponent() == null
&& r.intent.getPackage() == null
&& ((r.intent.getFlags()
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
&& !isSignaturePerm(r.requiredPermissions))) {
// 打破以上任意一個條件,即可把廣播發(fā)送給靜態(tài)接收器
mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
component.getPackageName());
skip = true;
}
}
}
// 跳過當(dāng)前廣播的發(fā)送
if (skip) {
// ...
return;
}
如果這個 reciever 是靜態(tài)接收器,那么在把廣播發(fā)送給它之前,首先得進行一大堆的檢測。最常見的就是權(quán)限,但是這里展示了一段 Android O+ 限制廣播發(fā)送給靜態(tài)接收器的限制,有興趣的讀者可以詳細分析。
接著看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
// ...
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// ...
}
boolean looped = false;
// 2. 通過 do-while 循環(huán),找到一個現(xiàn)在可以處理的廣播
do {
final long now = SystemClock.uptimeMillis();
// 獲取一個待處理的廣播
r = mDispatcher.getNextBroadcastLocked(now);
// ...
} while (r == null);
// 走到這里,表示已經(jīng)獲取了一個現(xiàn)在可以處理的廣播
int recIdx = r.nextReceiver++;
// 3. 在發(fā)送廣播之前,先發(fā)送一個超時消息
r.receiverTime = SystemClock.uptimeMillis();
// ...
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
setBroadcastTimeoutLocked(timeoutTime);
}
final BroadcastOptions brOptions = r.options;
// 4. 獲取一個 receiver
final Object nextReceiver = r.receivers.get(recIdx);
// 5. 如果這個接收器是動態(tài)接收器,先把廣播發(fā)送給它
// 注意,這里處理的是有序廣播發(fā)送給動態(tài)接收器的情況
if (nextReceiver instanceof BroadcastFilter) {
// ...
return;
}
// 走到這里,表示當(dāng)前的廣播接收器,是靜態(tài)接收器
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
boolean skip = false;
// 6. 檢測是否不需要把廣播發(fā)送給靜態(tài)接收器
// ... 省略一大堆的檢測代碼 ...
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid);
// ...
// 跳過當(dāng)前廣播的發(fā)送
if (skip) {
// ...
return;
}
// 現(xiàn)在可以把廣播發(fā)送給靜態(tài)接收器了
// ...
// 7. 靜態(tài)接收器的進程正在運行,那么就把廣播發(fā)送給它
if (app != null && app.getThread() != null && !app.isKilled()) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddAllowBackgroundActivityStartsToken(app, r);
// 發(fā)送廣播給廣播進程
processCurBroadcastLocked(r, app);
// 注意,廣播發(fā)送給這個靜態(tài)接收器后,直接結(jié)束此次廣播的處理
return;
} catch (RemoteException e) {
// ...
}
}
}
如果沒有限制,那么現(xiàn)在就可以把廣播發(fā)送給靜態(tài)接收器。
如果靜態(tài)接收器所在的進程已經(jīng)運行了,那么把廣播發(fā)送給這個進程,這個過程與前面發(fā)送廣播給動態(tài)接收器的過程非常類似,這里就不分析了。
注意,這里把廣播發(fā)送給一個靜態(tài)接收器,也是直接 return,懂了吧?
接著往下看
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
// ...
// 把并行廣播發(fā)送給動態(tài)接收器
while (mParallelBroadcasts.size() > 0) {
// ...
}
// 1. 處理 receiver 進程正在啟動的情況
if (mPendingBroadcast != null) {
// ...
}
boolean looped = false;
// 2. 通過 do-while 循環(huán),找到一個現(xiàn)在可以處理的廣播
do {
final long now = SystemClock.uptimeMillis();
// 獲取一個待處理的廣播
r = mDispatcher.getNextBroadcastLocked(now);
// ...
} while (r == null);
// 走到這里,表示已經(jīng)獲取了一個現(xiàn)在可以處理的廣播
int recIdx = r.nextReceiver++;
// 3. 在發(fā)送廣播之前,先發(fā)送一個超時消息
r.receiverTime = SystemClock.uptimeMillis();
// ...
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
setBroadcastTimeoutLocked(timeoutTime);
}
final BroadcastOptions brOptions = r.options;
// 4. 獲取一個 receiver
final Object nextReceiver = r.receivers.get(recIdx);
// 5. 如果這個接收器是動態(tài)接收器,先把廣播發(fā)送給它
// 注意,這里處理的是有序廣播發(fā)送給動態(tài)接收器的情況
if (nextReceiver instanceof BroadcastFilter) {
// ...
return;
}
// 走到這里,表示當(dāng)前的廣播接收器,是靜態(tài)接收器
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
boolean skip = false;
// 6. 檢測是否不需要把廣播發(fā)送給靜態(tài)接收器
// ...
// 跳過當(dāng)前廣播的發(fā)送
if (skip) {
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
r.manifestSkipCount++;
// 發(fā)送下一個廣播
scheduleBroadcastsLocked();
return;
}
// 現(xiàn)在可以把廣播發(fā)送給靜態(tài)接收器了
// ...
// 7. 靜態(tài)接收器的進程正在運行,那么就把廣播發(fā)送給它
if (app != null && app.getThread() != null && !app.isKilled()) {
// ...
}
// 8. 靜態(tài)接收器的進程沒有運行,fork it!
r.curApp = mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord("broadcast", r.curComponent), isActivityCapable
? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
// 處理 fork 進程失敗的情況
if (r.curApp == null) {
// ...
return;
}
maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
// fork 進程成功,保存廣播數(shù)據(jù),等待進程起來后,再處理這個廣播
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
剛才已經(jīng)處理了靜態(tài)接收器的進程存在的情況,那么現(xiàn)在處理進程不存在的情況,因此首先得 fork 進程。當(dāng)成功 fork 進程后,保存待發(fā)送的廣播的數(shù)據(jù),例如,用 mPendingBroadcast 保存廣播,然后當(dāng)進程啟動時,與 AMS 進行 attach application 時,會自動把廣播發(fā)送給該進程。這個過程后面會分析。
注意,此時函數(shù)已經(jīng)結(jié)束,而廣播正在發(fā)送給一個正在啟動的進程。很顯然,需要等待這個廣播的處理結(jié)果,才能繼續(xù)下一個廣播的發(fā)送,這也符合“串行”廣播的定義。
廣播發(fā)送給正在啟動的進程
剛才,我們分析到一個過程,當(dāng)靜態(tài)接收器所在的進程沒有啟動的時候,首先 fork 進程,那么廣播之后是如何發(fā)送給進程的呢?
首先,我們知道當(dāng)進程啟動后,會執(zhí)行 attach application 過程,最終會調(diào)用 AMS 如下方法
// ActivityManagerService.java
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// ...
try {
// ...
if (app.getIsolatedEntryPoint() != null) {
// ...
} else if (instr2 != null) {
// ...
} else {
// 初始化進程環(huán)境
thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.getCompat(), getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
app.getDisabledCompatChanges(), serializedSystemFontMap);
}
// ...
} catch (Exception e) {
// ...
}
// ....
// 處理正在等待宿主進程起來的廣播
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
// 發(fā)送隊列中正在等待進程起來的廣播
didSomething |= sendPendingBroadcastsLocked(app);
checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
} catch (Exception e) {
// ...
}
}
// ...
return true;
}
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
for (BroadcastQueue queue : mBroadcastQueues) {
didSomething |= queue.sendPendingBroadcastsLocked(app);
}
return didSomething;
}
看到了,AMS 首先對進程進行了初始化,然后就會把等待進程啟動的廣播,發(fā)送給它。
// BroadcastQueue.java
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
// mPendingBroadcast 保存的就是等待進程啟動啟動后,需要發(fā)送的廣播。
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
if (br.curApp != app) {
Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ app.processName + ", intended target is " + br.curApp.processName);
return false;
}
try {
mPendingBroadcast = null;
// 發(fā)送廣播給進程
processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
// ...
}
}
return didSomething;
}
mPendingBroadcast 保存的就是等待進程啟動啟動后,需要發(fā)送的廣播?,F(xiàn)在進程已經(jīng)啟動,立即發(fā)送廣播
// BroadcastQueue.java
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
final IApplicationThread thread = app.getThread();
if (thread == null) {
throw new RemoteException();
}
if (app.isInFullBackup()) {
skipReceiverLocked(r);
return;
}
// 更新正在處理廣播的 receiver 數(shù)據(jù)
r.receiver = thread.asBinder();
r.curApp = app;
// 保存當(dāng)前正在運行的 receiver
final ProcessReceiverRecord prr = app.mReceivers;
prr.addCurReceiver(r);
app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.updateLruProcessLocked(app, false, null);
mService.enqueueOomAdjTargetLocked(app);
mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
r.intent.setComponent(r.curComponent);
boolean started = false;
try {
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
// 通知進程啟動 receiver 來處理廣播
thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.mState.getReportedProcState());
started = true;
} finally {
if (!started) {
// ...
}
}
}
現(xiàn)在 AMS 通知 receiver 所在的進程來處理廣播
// ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
updateProcessState(processState, false);
// 廣播數(shù)據(jù)包裝成 ReceiverData
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
sendMessage(H.RECEIVER, r);
}
最終調(diào)用 handleReceiver() 處理廣播數(shù)據(jù)
private void handleReceiver(ReceiverData data) {
unscheduleGcIdler();
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManager.getService();
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
// 1. 創(chuàng)建 Application 對象,并調(diào)用 Application#onCreate()
app = packageInfo.makeApplication(false, mInstrumentation);
// ...
// 2. 創(chuàng)建 BroadcastReceiver 對象
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
// ...
}
try {
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
// 3. 執(zhí)行 BroadcastReceiver#onReceive()
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
// ...
} finally {
sCurrentBroadcastIntent.set(null);
}
// 4. 返回廣播的處理結(jié)果給 AMS
if (receiver.getPendingResult() != null) {
data.finish();
}
}
這里的過程很清晰明了吧,直接看最后一步,把廣播的處理結(jié)果反饋給 AMS
// BroadcastReceiver.java
public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
// ...
} else {
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
// ...
}
}
public void sendFinished(IActivityManager am) {
synchronized (this) {
if (mFinished) {
throw new IllegalStateException("Broadcast already finished");
}
mFinished = true;
try {
if (mResultExtras != null) {
mResultExtras.setAllowFds(false);
}
if (mOrderedHint) {
// 有序廣播的反饋
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast, mFlags);
} else {
// 非有序廣播的費奎
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}
現(xiàn)在看下 AMS 如何處理這個反饋的結(jié)果
// ActivityManagerService.java
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
// ...
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
BroadcastQueue queue;
synchronized(this) {
if (isOnOffloadQueue(flags)) {
queue = mOffloadBroadcastQueue;
} else {
queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
}
// 1. 匹配進程正在處理的廣播
r = queue.getMatchingOrderedReceiver(who);
// 2. 完成當(dāng)前 receiver 廣播的處理流程
if (r != null) {
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
// 3. 發(fā)送廣播給下一個 receiver,或者發(fā)送下一個廣播
if (doNext) {
r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
}
// updateOomAdjLocked() will be done here
trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
看到了,只有當(dāng)前 receiver 處理完廣播,才會發(fā)送廣播給下一個 receiver,這就是“串行”廣播的本質(zhì)。
廣播 ANR
最后,來探討一個廣播 ANR 的原理,本來我以為很簡單的,就是發(fā)送一個超時消息嘛。但是當(dāng)我細看的時候,我發(fā)現(xiàn)這個 ANR 設(shè)計的很巧妙,我覺得我們可以學(xué)習(xí)下,因此這里單獨拿出來分析。
這里,我得提醒大家一點,只有“串行”廣播才會發(fā)生 ANR,因為它要等待 receiver 的處理結(jié)果。
根據(jù)前面分析,“串行”廣播發(fā)送給 receiver 前,會發(fā)送一個超時消息,如下
// BroadcastQueue.java
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
setBroadcastTimeoutLocked(timeoutTime);
}
當(dāng)這個消息被執(zhí)行的時候,會調(diào)用如下代碼
// BroadcastQueue.java
final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
return;
}
long now = SystemClock.uptimeMillis();
// 獲取當(dāng)前正在處理的廣播
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
// 系統(tǒng)還沒有就緒
if (!mService.mProcessesReady) {
return;
}
// 廣播超時被豁免
if (r.timeoutExempt) {
if (DEBUG_BROADCAST) {
Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ r.intent.getAction());
}
return;
}
// 1. 廣播沒有超時
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (timeoutTime > now) {
// 發(fā)送下一個超時消息
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
if (r.state == BroadcastRecord.WAITING_SERVICES) {
// ...
return;
}
// 2. 走到這里,表示廣播超時,觸發(fā) ANR
final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
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;
}
logBroadcastReceiverDiscardLocked(r);
// 獲取 receiver 進程
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;
}
// 強制結(jié)束當(dāng)前廣播的發(fā)送流程
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
// 調(diào)度下一次的廣播發(fā)送
scheduleBroadcastsLocked();
// app 不處于 debug 模式,引發(fā) ANR
if (!debugging && anrMessage != null) {
mService.mAnrHelper.appNotResponding(app, anrMessage);
}
}
第2步,引發(fā) ANR ,很簡單,就是因為整個發(fā)送與反饋過程超時了。
而第1步,就是處理不超時的情況。這里大家是不是很疑惑,移除超時消息不是在接收到廣播反饋后進行的嗎? 我可以負責(zé)地告訴你,并不是!那這里第1步怎么理解呢?
首先這個超時消息一定觸發(fā),但是觸發(fā)這個超時消息,并不代表一定會引發(fā) ANR。
假如當(dāng)前 receiver 的廣播處理流程,在超時時間之前就完成了,那么 AMS 會調(diào)度廣播發(fā)送給下一個 receiver。
于是,針對下一個 receiver ,會更新 r.receiverTime,那么第一步此時計算出來的 timeoutTime 是下一個 receiver 的廣播超時時間,很顯然是大于 now 的,于是就不會走第2步的 ANR 流程。
最后利用這個 timeoutTime,為下一個 receiver 再發(fā)送一個超時消息,簡直是完美!
至于為何不在廣播反饋的時候,移除這個超時消息,我心中有一點小小的想法,但是也不能確定是不是這個原因,才這樣設(shè)計的。不過,對我來說,這一招,我算是學(xué)會了。
最后,提醒讀者,廣播接收器是在主線程中運行的,不要執(zhí)行耗時任務(wù),或者潛在耗時的任務(wù),我在工作中看到了無數(shù)血與淚的案例。
結(jié)束
本文從整體上分析了“串行”和“并行”廣播的發(fā)送流程,并以此為基礎(chǔ),解析了“串行”廣播的 ANR 原理。
但是,還有一些廣播的細節(jié),我并沒有分析,例如 Android O+ 如何限制廣播發(fā)送給靜態(tài)接收器,又例如,什么情況下,會把廣播延遲發(fā)送給 app。只要你站在我的肩膀上,就可以自行分析這些細節(jié)。
另外,我在分析的時候,有個優(yōu)化廣播發(fā)送的想法,如果廣播有多個app的靜態(tài)接收器,我可以建立一個機制,優(yōu)先把廣播發(fā)送給某些 app,并且對于這些 app,我不需要等待它反饋廣播處理結(jié)果,就可以發(fā)送廣播給下一個接收器。如果以后工作有需要,我會嘗試做一做,更多關(guān)于ActivityManagerService廣播的資料請關(guān)注腳本之家其它相關(guān)文章!
- Android registerForActivityResult動態(tài)申請權(quán)限案例詳解
- ActivityManagerService廣播注冊與發(fā)送示例解析
- ActivityManagerService之Service啟動過程解析
- Android面向單Activity開發(fā)示例解析
- Vue3源碼分析reactivity實現(xiàn)方法示例
- vue3源碼分析reactivity實現(xiàn)原理
- Android10 App啟動Activity源碼分析
- Android?Activity共享元素動畫示例解析
- Android?registerForActivityResult新用法實現(xiàn)兩個Activity間數(shù)據(jù)傳遞
相關(guān)文章
Android TextView漸變顏色和方向及動畫效果的設(shè)置詳解
TextView的在安卓中可以理解為一個文本視圖控件,Android的視圖控件的基類是View類,可以理解的TextView是View的子類。我們通常在.XML布局文件中會為文本視圖控件指定各種屬性來設(shè)置它的樣式,今天我們要講的當(dāng)然不是傳統(tǒng)常見的那種,將會帶有漸變顏色和方向及動畫效果2021-11-11
EasyValidate優(yōu)雅地校驗提交數(shù)據(jù)完整性
這篇文章主要介紹了EasyValidate優(yōu)雅地校驗提交數(shù)據(jù)完整性,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
Android如何在root設(shè)備上開啟ViewServer詳解
這篇文章主要給大家介紹了關(guān)于Android中如何在root設(shè)備上開啟ViewServer的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-12-12
Android LayoutInflater加載布局詳解及實例代碼
這篇文章主要介紹了Android LayoutInflater加載布局詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02
Android平臺預(yù)置GMS包后關(guān)機鬧鐘失效問題及解決方法
這篇文章主要介紹了Android平臺預(yù)置GMS包后,關(guān)機鬧鐘失效,本文給大家分享問題原因及解決方法,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
Android編程操作嵌入式關(guān)系型SQLite數(shù)據(jù)庫實例詳解
這篇文章主要介紹了Android編程操作嵌入式關(guān)系型SQLite數(shù)據(jù)庫的方法,結(jié)合實例形式較為詳細的分析了Android操作SQLite數(shù)據(jù)庫的基本技巧與相關(guān)注意事項,需要的朋友可以參考下2016-01-01
Android布局之絕對布局AbsoluteLayout詳解
這篇文章主要為大家詳細介紹了Android布局之絕對布局AbsoluteLayout的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10
Android設(shè)置當(dāng)TextView中的文字超過TextView的容量時用省略號代替
這篇文章主要介紹了Android設(shè)置當(dāng)TextView中的文字超過TextView的容量時用省略號代替 ,需要的朋友可以參考下2017-03-03

