ActivityManagerService广播并行发送与串行发送示例解析
"并行"广播的发送
本文以 ActivityManagerService之广播(1): 注册与发送 为基础,分析“串行”和“并行”广播的发送流程,并介绍广播 ANR 的原理。
// 1. 获取广播队列 final BroadcastQueue queue = broadcastQueueForIntent(intent); // 2. 创建广播记录 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. 调度发送广播 queue.scheduleBroadcastsLocked();
第3步,把广播记录保存到并行队列中
// BroadcastQueue.java public void enqueueParallelBroadcastLocked(BroadcastRecord r) { // mParallelBroadcasts 类型为 ArrayList<BroadcastRecord> mParallelBroadcasts.add(r); enqueueBroadcastHelper(r); }
第4步,调度发送广播,最终会调用如下方法
// BroadcastQueue.java // 此时,参数 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); // 广播发送给动态接收器 deliverToRegisteredReceiverLocked(r, (BroadcastFilter) target, false, i); } addBroadcastToHistoryLocked(r); } // ... 省略"串行"广播的发送 ... }
虽然名为“并行”广播,但是仍然是从队列取出广播,然后逐个发送给动态接收器。很显然,这里的行为与“并行”的含义并不一致?那么广播的“并行”发送究竟是什么意思?
接着看“并行”广播如何发送给动态接收器的
private void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered, int index) { // ... 省略一大堆的权限或者异常检测 ... // 一个广播可能有多个接收者,因此需要一个数组来保存发送的状态 r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED; // ordered 目前为 false if (ordered) { // ... } } else if (filter.receiverList.app != null) { // 马上要发送广播给接收方,因此要暂时解冻接收方的进程 mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app); } try { if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) { // ... 处于备份状态中 ... } else { r.receiverTime = SystemClock.uptimeMillis(); // 保存允许从后台启动activity的token maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r); // 添加到省电模式白名单中 maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options); // 执行广播的发送 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) { // ... } }
抛开一些细节,直接看 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 { // 动态广播接收器的进程,应该是存在的 if (app != null) { final IApplicationThread thread = app.getThread(); if (thread != null) { try { // 发送广播给接收方进程 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 接口,发送广播给进程。这个过程,暂时先不分析,后面会分析到。
那么,现在来回答一下,何为“并行”广播?其实这个答案,我也是对比了串行广播的发送过程,才得出来的。所谓的"并行"发送,实际上就是把广播逐个发送给动态接收器,但是不需要等待前一个接收器反馈处理结果,就可以发送下一个。而“串行”广播的发送,是需要等待前一个广播接收器反馈处理结果后,才能调度发送下一个广播。
“串行”广播的发送
// 1.获取广播队列 BroadcastQueue queue = broadcastQueueForIntent(intent); // 2.创建广播记录 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.调度发送广播 queue.scheduleBroadcastsLocked();
第3步,广播加入到串行队列中
// BroadcastQueue.java public void enqueueOrderedBroadcastLocked(BroadcastRecord r) { mDispatcher.enqueueOrderedBroadcastLocked(r); enqueueBroadcastHelper(r); }
// BroadcastDispatcher.java void enqueueOrderedBroadcastLocked(BroadcastRecord r) { mOrderedBroadcasts.add(r); }
并行发送的广播保存到 BroadcastQueue#mParallelBroadcasts 中,而串行发送的广播保存到 BroadcastDispatcher#mOrderedBroadcasts 中,为何要这样设计呢?有兴趣的读者可以研究下。
第4步,“串行”广播的调度发送,仍然使用的是 processNextBroadcastLocked() 方法,但是代码量是非常的大,下面将把函数分段解析。
processNextBroadcastLocked() 函数有400多行代码,这个函数里有很多东西都可以抽出来的,但是随着版本的更新,这块代码一直没有优化过。
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { BroadcastRecord r; mService.updateCpuStats(); if (fromMsg) { mBroadcastsScheduled = false; } // 把并行广播发送给动态接收器 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) { // 进程仍然存活,结束此次广播的处理流程,继续等待 // 等待什么呢?等待广播进程起来,并与 AMS 完成 attach application // 在 attach application 的过程中,会完成广播的发送 return; } else { // 进程死亡,继续处理下一个广播 mPendingBroadcast.state = BroadcastRecord.IDLE; mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex; mPendingBroadcast = null; } }
当发送一个广播给 receiver 时,如果 receiver 进程没有启动,那么会先 fork 一个 receiver 进程,然后用 mPendingBroadcast 保存待发送的广播。当 receiver 进程起来的时候,会与 AMS 执行 attach application 过程,在这个过程中,会自动把 mPendingBroadcast 保存的广播发送给 receiver 进程。
因此,这里检测到 mPendingBroadcast 不为 null 时,那么 receiver 进程肯定在启动中,只要 receiver 进程没有死亡,就什么也不用做,因为广播会自动发送给 receiver 进程。
接着看下一步的处理
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { // ... // 把并行广播发送给动态接收器 while (mParallelBroadcasts.size() > 0) { // ... } // 1. 处理 receiver 进程正在启动的情况 if (mPendingBroadcast != null) { // ... } boolean looped = false; // 2. 通过 do-while 循环,找到一个现在可以处理的广播 do { final long now = SystemClock.uptimeMillis(); // 获取一个待处理的广播 r = mDispatcher.getNextBroadcastLocked(now); if (r == null) { // ... 没有广播需要处理 ... return; } boolean forceReceive = false; // 处理严重超时的广播,有两种情况 // 一种情况是,在系统还没有起来前,发送的广播得不到执行,发生严重超时 // 另外一种情况是,在系统起来后,有一些超时豁免的广播,发生了严重超时 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; } // 当前广播因为某种原因,终止处理,然后处理下一个广播 if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) { // ... // 通知 BroadcastDispatcher ,不处理这个广播了 mDispatcher.retireBroadcastLocked(r); r = null; looped = true; // 下一次循环,获取下一个广播来处理 continue; } // 处理推迟发送广播的情况 if (!r.deferred) { final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver)); if (mDispatcher.isDeferringLocked(receiverUid)) { // ... // 保存推迟发送的广播 mDispatcher.addDeferredBroadcast(receiverUid, defer); r = null; looped = true; // 下一次循环时,获取下一个广播来处理 continue; } } } while (r == null);
先从整体看,通过一个 do-while 循环,最终是为了找到下一个处理的广播。为何要用一个循环来寻找呢? 因为广播可能没有接收器,或者已经严重超时,又或者广播需要推迟发送。所以要通过一个循环,找到一个能立即发送的广播。
由于本文主要是为了分析广播发送的整体流程,对于有些细节,只做注释而不做细致分析。需要深入研究的读者,可以在本文的基础上继续分析。
继续接着看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { BroadcastRecord r; mService.updateCpuStats(); if (fromMsg) { mBroadcastsScheduled = false; } // 把并行广播发送给动态接收器 while (mParallelBroadcasts.size() > 0) { // ... } // 1. 处理 receiver 进程正在启动的情况 if (mPendingBroadcast != null) { // ... } boolean looped = false; // 2. 通过 do-while 循环,找到一个现在可以处理的广播 do { final long now = SystemClock.uptimeMillis(); // 获取一个待处理的广播 r = mDispatcher.getNextBroadcastLocked(now); // ... } while (r == null); // 走到这里,表示已经获取了一个现在可以处理的广播 int recIdx = r.nextReceiver++; // 3. 在发送广播之前,先发送一个超时消息 r.receiverTime = SystemClock.uptimeMillis(); if (recIdx == 0) { // 在广播开始发送给第一个接收器时,记录发送的时间 r.dispatchTime = r.receiverTime; r.dispatchClockTime = System.currentTimeMillis(); } if (! mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mConstants.TIMEOUT; setBroadcastTimeoutLocked(timeoutTime); }
在广播发送给一个 receiver 之前,会先发送一个超时消息。从广播准备发送给一个 receiver 算起,到 receiver 处理完广播,并反馈给 AMS,如果这个时间段超过了一个时间阈值,就会引发 ANR。触发 ANR 的代码设计非常巧妙,后面会具体分析这个过程。
接着看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { // ... // 把并行广播发送给动态接收器 while (mParallelBroadcasts.size() > 0) { // ... } // 1. 处理 receiver 进程正在启动的情况 if (mPendingBroadcast != null) { // ... } boolean looped = false; // 2. 通过 do-while 循环,找到一个现在可以处理的广播 do { // ... } while (r == null); int recIdx = r.nextReceiver++; // ... // 3. 在发送广播之前,先发送一个超时消息 // 当广播处理超时时,会触发 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. 如果这个接收器是动态接收器,先把广播发送给它 // 注意,这里处理的是有序广播发送给动态接收器的情况 if (nextReceiver instanceof BroadcastFilter) { BroadcastFilter filter = (BroadcastFilter)nextReceiver; // 发送广播给动态接收器 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); } } // 注意,把广播发送给 动态receiver 后,直接返回 return; }
现在一切就绪,那么开始获取一个 receiver,当这个 receiver 是一个动态接收器时,直接发送广播给它,这个发送过程前面已经分析过。
注意,这里处理的情况是,把有序广播发送给动态接收器。并且发送完成后,直接 return, 也就是结束了此次广播的发送流程。
一个广播可能有多个接收器,为何这里只发送给一个动态接收器,就直接返回了? 这就是从“串行”广播的本质,需要等待当前的广播接收器处理完广播,并返回结果后,才能把广播发送给下一个广播接收器。
接着看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { // ... // 把并行广播发送给动态接收器 while (mParallelBroadcasts.size() > 0) { // ... } // 1. 处理 receiver 进程正在启动的情况 if (mPendingBroadcast != null) { // ... } boolean looped = false; // 2. 通过 do-while 循环,找到一个现在可以处理的广播 do { final long now = SystemClock.uptimeMillis(); // 获取一个待处理的广播 r = mDispatcher.getNextBroadcastLocked(now); // ... } while (r == null); // 走到这里,表示已经获取了一个现在可以处理的广播 int recIdx = r.nextReceiver++; // 3. 在发送广播之前,先发送一个超时消息 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. 如果这个接收器是动态接收器,先把广播发送给它 // 注意,这里处理的是有序广播发送给动态接收器的情况 if (nextReceiver instanceof BroadcastFilter) { // ... return; } // 走到这里,表示当前的广播接收器,是静态接收器 // 获取静态接收器的信息 ResolveInfo info = (ResolveInfo)nextReceiver; ComponentName component = new ComponentName( info.activityInfo.applicationInfo.packageName, info.activityInfo.name); boolean skip = false; // 6. 检测是否不需要把广播发送给静态接收器 // ... 省略一大堆的检测代码 ... String targetProcess = info.activityInfo.processName; ProcessRecord app = mService.getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid); if (!skip) { // 检测是否允许把广播发送给静态接收器 final int allowed = mService.getAppStartModeLOSP( info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false); // 例如,大于等于 O+ 版本的 app ,不允许广播发送给静态接收器 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))) { // 打破以上任意一个条件,即可把广播发送给静态接收器 mService.addBackgroundCheckViolationLocked(r.intent.getAction(), component.getPackageName()); skip = true; } } } // 跳过当前广播的发送 if (skip) { // ... return; }
如果这个 reciever 是静态接收器,那么在把广播发送给它之前,首先得进行一大堆的检测。最常见的就是权限,但是这里展示了一段 Android O+ 限制广播发送给静态接收器的限制,有兴趣的读者可以详细分析。
接着看下一步
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { // ... // 把并行广播发送给动态接收器 while (mParallelBroadcasts.size() > 0) { // ... } // 1. 处理 receiver 进程正在启动的情况 if (mPendingBroadcast != null) { // ... } boolean looped = false; // 2. 通过 do-while 循环,找到一个现在可以处理的广播 do { final long now = SystemClock.uptimeMillis(); // 获取一个待处理的广播 r = mDispatcher.getNextBroadcastLocked(now); // ... } while (r == null); // 走到这里,表示已经获取了一个现在可以处理的广播 int recIdx = r.nextReceiver++; // 3. 在发送广播之前,先发送一个超时消息 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. 如果这个接收器是动态接收器,先把广播发送给它 // 注意,这里处理的是有序广播发送给动态接收器的情况 if (nextReceiver instanceof BroadcastFilter) { // ... return; } // 走到这里,表示当前的广播接收器,是静态接收器 ResolveInfo info = (ResolveInfo)nextReceiver; ComponentName component = new ComponentName( info.activityInfo.applicationInfo.packageName, info.activityInfo.name); boolean skip = false; // 6. 检测是否不需要把广播发送给静态接收器 // ... 省略一大堆的检测代码 ... String targetProcess = info.activityInfo.processName; ProcessRecord app = mService.getProcessRecordLocked(targetProcess, info.activityInfo.applicationInfo.uid); // ... // 跳过当前广播的发送 if (skip) { // ... return; } // 现在可以把广播发送给静态接收器了 // ... // 7. 静态接收器的进程正在运行,那么就把广播发送给它 if (app != null && app.getThread() != null && !app.isKilled()) { try { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats); maybeAddAllowBackgroundActivityStartsToken(app, r); // 发送广播给广播进程 processCurBroadcastLocked(r, app); // 注意,广播发送给这个静态接收器后,直接结束此次广播的处理 return; } catch (RemoteException e) { // ... } } }
如果没有限制,那么现在就可以把广播发送给静态接收器。
如果静态接收器所在的进程已经运行了,那么把广播发送给这个进程,这个过程与前面发送广播给动态接收器的过程非常类似,这里就不分析了。
注意,这里把广播发送给一个静态接收器,也是直接 return,懂了吧?
接着往下看
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { // ... // 把并行广播发送给动态接收器 while (mParallelBroadcasts.size() > 0) { // ... } // 1. 处理 receiver 进程正在启动的情况 if (mPendingBroadcast != null) { // ... } boolean looped = false; // 2. 通过 do-while 循环,找到一个现在可以处理的广播 do { final long now = SystemClock.uptimeMillis(); // 获取一个待处理的广播 r = mDispatcher.getNextBroadcastLocked(now); // ... } while (r == null); // 走到这里,表示已经获取了一个现在可以处理的广播 int recIdx = r.nextReceiver++; // 3. 在发送广播之前,先发送一个超时消息 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. 如果这个接收器是动态接收器,先把广播发送给它 // 注意,这里处理的是有序广播发送给动态接收器的情况 if (nextReceiver instanceof BroadcastFilter) { // ... return; } // 走到这里,表示当前的广播接收器,是静态接收器 ResolveInfo info = (ResolveInfo)nextReceiver; ComponentName component = new ComponentName( info.activityInfo.applicationInfo.packageName, info.activityInfo.name); boolean skip = false; // 6. 检测是否不需要把广播发送给静态接收器 // ... // 跳过当前广播的发送 if (skip) { r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED; r.receiver = null; r.curFilter = null; r.state = BroadcastRecord.IDLE; r.manifestSkipCount++; // 发送下一个广播 scheduleBroadcastsLocked(); return; } // 现在可以把广播发送给静态接收器了 // ... // 7. 静态接收器的进程正在运行,那么就把广播发送给它 if (app != null && app.getThread() != null && !app.isKilled()) { // ... } // 8. 静态接收器的进程没有运行,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 进程成功,保存广播数据,等待进程起来后,再处理这个广播 mPendingBroadcast = r; mPendingBroadcastRecvIndex = recIdx; }
刚才已经处理了静态接收器的进程存在的情况,那么现在处理进程不存在的情况,因此首先得 fork 进程。当成功 fork 进程后,保存待发送的广播的数据,例如,用 mPendingBroadcast 保存广播,然后当进程启动时,与 AMS 进行 attach application 时,会自动把广播发送给该进程。这个过程后面会分析。
注意,此时函数已经结束,而广播正在发送给一个正在启动的进程。很显然,需要等待这个广播的处理结果,才能继续下一个广播的发送,这也符合“串行”广播的定义。
广播发送给正在启动的进程
刚才,我们分析到一个过程,当静态接收器所在的进程没有启动的时候,首先 fork 进程,那么广播之后是如何发送给进程的呢?
首先,我们知道当进程启动后,会执行 attach application 过程,最终会调用 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 { // 初始化进程环境 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 { // 发送队列中正在等待进程起来的广播 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 首先对进程进行了初始化,然后就会把等待进程启动的广播,发送给它。
// BroadcastQueue.java public boolean sendPendingBroadcastsLocked(ProcessRecord app) { boolean didSomething = false; // mPendingBroadcast 保存的就是等待进程启动启动后,需要发送的广播。 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; // 发送广播给进程 processCurBroadcastLocked(br, app); didSomething = true; } catch (Exception e) { // ... } } return didSomething; }
mPendingBroadcast 保存的就是等待进程启动启动后,需要发送的广播。现在进程已经启动,立即发送广播
// 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 数据 r.receiver = thread.asBinder(); r.curApp = app; // 保存当前正在运行的 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) { // ... } } }
现在 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); // 广播数据包装成 ReceiverData ReceiverData r = new ReceiverData(intent, resultCode, data, extras, sync, false, mAppThread.asBinder(), sendingUser); r.info = info; r.compatInfo = compatInfo; sendMessage(H.RECEIVER, r); }
最终调用 handleReceiver() 处理广播数据
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. 创建 Application 对象,并调用 Application#onCreate() app = packageInfo.makeApplication(false, mInstrumentation); // ... // 2. 创建 BroadcastReceiver 对象 receiver = packageInfo.getAppFactory() .instantiateReceiver(cl, data.info.name, data.intent); } catch (Exception e) { // ... } try { sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); // 3. 执行 BroadcastReceiver#onReceive() receiver.onReceive(context.getReceiverRestrictedContext(), data.intent); } catch (Exception e) { // ... } finally { sCurrentBroadcastIntent.set(null); } // 4. 返回广播的处理结果给 AMS if (receiver.getPendingResult() != null) { data.finish(); } }
这里的过程很清晰明了吧,直接看最后一步,把广播的处理结果反馈给 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) { } } }
现在看下 AMS 如何处理这个反馈的结果
// 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. 完成当前 receiver 广播的处理流程 if (r != null) { doNext = r.queue.finishReceiverLocked(r, resultCode, resultData, resultExtras, resultAbort, true); } // 3. 发送广播给下一个 receiver,或者发送下一个广播 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); } }
看到了,只有当前 receiver 处理完广播,才会发送广播给下一个 receiver,这就是“串行”广播的本质。
广播 ANR
最后,来探讨一个广播 ANR 的原理,本来我以为很简单的,就是发送一个超时消息嘛。但是当我细看的时候,我发现这个 ANR 设计的很巧妙,我觉得我们可以学习下,因此这里单独拿出来分析。
这里,我得提醒大家一点,只有“串行”广播才会发生 ANR,因为它要等待 receiver 的处理结果。
根据前面分析,“串行”广播发送给 receiver 前,会发送一个超时消息,如下
// BroadcastQueue.java if (! mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mConstants.TIMEOUT; setBroadcastTimeoutLocked(timeoutTime); }
当这个消息被执行的时候,会调用如下代码
// BroadcastQueue.java final void broadcastTimeoutLocked(boolean fromMsg) { if (fromMsg) { mPendingBroadcastTimeoutMessage = false; } if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) { return; } long now = SystemClock.uptimeMillis(); // 获取当前正在处理的广播 BroadcastRecord r = mDispatcher.getActiveBroadcastLocked(); if (fromMsg) { // 系统还没有就绪 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) { // 发送下一个超时消息 setBroadcastTimeoutLocked(timeoutTime); return; } } if (r.state == BroadcastRecord.WAITING_SERVICES) { // ... return; } // 2. 走到这里,表示广播超时,触发 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; } // 强制结束当前广播的发送流程 finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); // 调度下一次的广播发送 scheduleBroadcastsLocked(); // app 不处于 debug 模式,引发 ANR if (!debugging && anrMessage != null) { mService.mAnrHelper.appNotResponding(app, anrMessage); } }
第2步,引发 ANR ,很简单,就是因为整个发送与反馈过程超时了。
而第1步,就是处理不超时的情况。这里大家是不是很疑惑,移除超时消息不是在接收到广播反馈后进行的吗? 我可以负责地告诉你,并不是!那这里第1步怎么理解呢?
首先这个超时消息一定触发,但是触发这个超时消息,并不代表一定会引发 ANR。
假如当前 receiver 的广播处理流程,在超时时间之前就完成了,那么 AMS 会调度广播发送给下一个 receiver。
于是,针对下一个 receiver ,会更新 r.receiverTime,那么第一步此时计算出来的 timeoutTime 是下一个 receiver 的广播超时时间,很显然是大于 now 的,于是就不会走第2步的 ANR 流程。
最后利用这个 timeoutTime,为下一个 receiver 再发送一个超时消息,简直是完美!
至于为何不在广播反馈的时候,移除这个超时消息,我心中有一点小小的想法,但是也不能确定是不是这个原因,才这样设计的。不过,对我来说,这一招,我算是学会了。
最后,提醒读者,广播接收器是在主线程中运行的,不要执行耗时任务,或者潜在耗时的任务,我在工作中看到了无数血与泪的案例。
结束
本文从整体上分析了“串行”和“并行”广播的发送流程,并以此为基础,解析了“串行”广播的 ANR 原理。
但是,还有一些广播的细节,我并没有分析,例如 Android O+ 如何限制广播发送给静态接收器,又例如,什么情况下,会把广播延迟发送给 app。只要你站在我的肩膀上,就可以自行分析这些细节。
另外,我在分析的时候,有个优化广播发送的想法,如果广播有多个app的静态接收器,我可以建立一个机制,优先把广播发送给某些 app,并且对于这些 app,我不需要等待它反馈广播处理结果,就可以发送广播给下一个接收器。如果以后工作有需要,我会尝试做一做,更多关于ActivityManagerService广播的资料请关注脚本之家其它相关文章!
相关文章
Android SDK Manager更新、下载速度慢问题解决办法
这篇文章主要介绍了Android SDK Manager更新、下载速度慢问题解决办法的相关资料,需要的朋友可以参考下2017-05-05Android提高之BroadcastReceiver实例详解
这篇文章主要介绍了Android的BroadcastReceiver用法,在Android的项目开发中是比较实用的功能,需要的朋友可以参考下2014-08-08Kotlin文件读写与SharedPreferences存储功能实现方法
SharedPreferences是安卓平台上一个轻量级的存储类,用来保存应用的一些常用配置,比如Activity状态,Activity暂停时,将此activity的状态保存到SharedPereferences中;当Activity重载,系统回调方法onSaveInstanceState时,再从SharedPreferences中将值取出2022-12-12
最新评论