本分对广播分发的流程中的超时逻辑进行分析。
广播 ANR 只存在于有序广播中,无序广播并不会触发 ANR。其中 ANR 有两处可以触发,接收者处理广播超时和该广播处理超时。
广播分发超时
这个是指分发这个广播总时间超时,其逻辑在 BroadcastQueue#processNextBroadcastLocked
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) { if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) { Slog.w(TAG, "Hung broadcast [" + mQueueName + "] discarded after timeout failure:" + " now=" + now + " dispatchTime=" + r.dispatchTime + " startTime=" + r.receiverTime + " intent=" + r.intent + " numReceivers=" + numReceivers + " nextReceiver=" + r.nextReceiver + " state=" + r.state); broadcastTimeoutLocked(false); forceReceive = true; r.state = BroadcastRecord.IDLE; } }
|
r.dispatchTime 的赋值也是在 processNextBroadcastLocked 中,在第一次分发有序广播给接收者的时候:
1 2 3 4
| r.receiverTime = SystemClock.uptimeMillis(); if (recIdx == 0) { r.dispatchTime = r.receiverTime; }
|
这里也可以发现当总耗时超时后会直接出发 ANR。
接收者处理超时
这个就是平时遇到的最多的一种情况,同样是在 BroadcastQueue#processNextBroadcastLocked
中:
1 2 3 4 5 6 7 8 9 10 11 12
| if (! mPendingBroadcastTimeoutMessage) { long timeoutTime = r.receiverTime + mConstants.TIMEOUT; setBroadcastTimeoutLocked(timeoutTime); }
final void setBroadcastTimeoutLocked(long timeoutTime) { if (! mPendingBroadcastTimeoutMessage) { Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); mHandler.sendMessageAtTime(msg, timeoutTime); mPendingBroadcastTimeoutMessage = true; } }
|
通过 mPendingBroadcastTimeoutMessage 来判断是否已经发送了超时消息,这里延迟时间就是该接收者的接受时间加超时时间(后台广播为 10s,前台广播为 60s)。
触发 ANR
如果在规定时间内一个接收者有处理完成,则下一个接收者处理时并不会重新开始计时,而是会在 BROADCAST_TIMEOUT_MSG 消息被处理的时候,即 BroadcastQueue#broadcastTimeoutLocked 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| final void broadcastTimeoutLocked(boolean fromMsg) { if (fromMsg) { mPendingBroadcastTimeoutMessage = false; } long now = SystemClock.uptimeMillis(); BroadcastRecord r = mDispatcher.getActiveBroadcastLocked(); if (fromMsg) { if (!mService.mProcessesReady) return; if (r.timeoutExempt) return; long timeoutTime = r.receiverTime + mConstants.TIMEOUT; if (timeoutTime > now) { setBroadcastTimeoutLocked(timeoutTime); return; } }
if (r.state == BroadcastRecord.WAITING_SERVICES) { Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null ? r.curComponent.flattenToShortString() : "(null)")); r.curComponent = null; r.state = BroadcastRecord.IDLE; processNextBroadcast(false); return; } anrMessage = "Broadcast of " + r.intent.toString(); }
if (mPendingBroadcast == r) { mPendingBroadcast = null; } finishReceiverLocked(r, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); scheduleBroadcastsLocked(); if (!debugging && anrMessage != null) { mService.mAnrHelper.appNotResponding(app, anrMessage); } }
|
广播 ANR 分析
如之前的流程,有序广播在接受过程中存在跨进程通信,还需要通过接收者进程的 Handler 来进行分发,并且最主要的是流程还在 AMS 时就已经开始了超时的计时,因此这个过程中只要有一个地方有耗时,即使此时 onReceive() 还没有被调用,也会有 ANR 发生。并且原生逻辑中关键部分的 Log 都是由开关控制,如果不是应用自身的逻辑问题,那么是很难定位具体卡在了哪一个环节,这里也可以借助 systrace 或 perfetto 来辅助分析。