Android15的廣播ANR源碼流程
跟了下實際代碼的流程,大概如下哈:
App.sendBroadcast() ?// 應用發起廣播
→ AMS.broadcastIntentWithFeature() ?// 通過Binder IPC進入system_server進程
→ AMS.broadcastIntentLocked() ?// 權限校驗+廣播分類(前臺/后臺)
→ BroadcastQueueModernImpl.enqueueBroadcastLocked() ?// 按進程隔離的隊列管理
→ BroadcastQueueModernImpl.updateRunningListLocked() ?// 檢查進程活躍狀態
→ BroadcastQueueModernImpl.scheduleReceiverWarmLocked() ?// 預熱進程(若未啟動)
→ BroadcastQueueModernImpl.dispatchReceivers() ?// 關鍵:啟動超時檢測(前臺10s/后臺60s)
? ?├─ 通過Handler發送MSG_DELIVERY_TIMEOUT延遲消息
? ?└─ 豁免條件:系統未就緒或廣播標記為timeoutExempt
→ IApplicationThread.scheduleRegisteredReceiver() ?// Binder投遞到目標進程
→ ActivityThread.post(Args.run()) ?// 主線程消息隊列調度
→ Args.run() ?// 封裝廣播處理邏輯
→ BroadcastReceiver.onReceive() ?// 開發者代碼執行點(耗時操作直接觸發ANR)
→ AMS.finishReceiver() ?// 正常完成通知
→ BroadcastQueueModernImpl.finishReceiverLocked() ?// 移除超時檢測(Handler取消MSG_DELIVERY_TIMEOUT)
? ?└─ 若超時未完成→ AMS.appNotResponding() ?// 觸發ANR彈窗(含Intent/接收者類名等上下文)
源碼分析
→ App.sendBroadcast()?
→ → AMS.broadcastIntentWithFeature()?
→ → → AMS.broadcastIntentLocked() /broadcastIntentLockedTraced()
→ → → → BroadcastQueueModernImpl.enqueueBroadcastLocked()
→ → → → → BroadcastQueueModernImpl.updateRunningListLocked()?
→ → → → → →BroadcastQueueModernImpl.scheduleReceiverWarmLocked()?
→ → → → → → BroadcastQueueModernImpl.dispatchReceivers()?
→ → → → → → → IApplicationThread.scheduleRegisteredReceiver()
→→ → → → → → → → ActivityThread.post()
→→ → → → → → → → → Args.run()?
→ → → → → → → → → → BroadcastReceiver.onReceive()
→ → → → → → → → → → → AMS.finishReceiver()?
→ → → → → → → → → → → →BroadcastQueueModernImpl.finishReceiverLocked()?
→ → → → → → → → → → → →→AMS.appNotResponding(queue.app, tr)
0. 廣播產生并發送給AMS
應用或系統廣播產生并發送給AMS
源碼路徑:frameworks/base/core/java/android/app/ContextImpl.java
@Override
public void sendBroadcast(Intent intent) {
? ? warnIfCallingFromSystemProcess(); // 防止系統進程誤用普通API
? ? String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
? ? try {
? ? ? ? intent.prepareToLeaveProcess(this); // 清理不可序列化對象,確保跨進程傳輸
? ? ? ? // 通過Binder調用AMS的廣播接口
? ? ? ? ActivityManager.getService().broadcastIntentWithFeature(
? ? ? ? ? ? mMainThread.getApplicationThread(), // 調用方線程標識
? ? ? ? ? ? getAttributionTag(), ? ? ? ? ? ? ? ?// 權限追蹤標簽
? ? ? ? ? ? intent, resolvedType, null, Activity.RESULT_OK,?
? ? ? ? ? ? null, null, null, null, null, AppOpsManager.OP_NONE,?
? ? ? ? ? ? null, false, false, getUserId());
? ? } catch (RemoteException e) {
? ? ? ? throw e.rethrowFromSystemServer();
? ? }
}
1. AMS投遞廣播到隊列并啟動超時檢測
源碼路徑:frameworks/base/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@GuardedBy("mService")
private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,
? ? ? ? @NonNull BroadcastRecord r, int index) throws BroadcastRetryException {
? ? final ProcessRecord app = queue.app;
? ? // 僅在滿足條件時啟動超時檢測
? ? if (mService.mProcessesReady && !r.timeoutExempt && !r.isAssumedDelivered(index)) {
? ? ? ? queue.setTimeoutScheduled(true);
? ? ? ? // 區分前臺/后臺廣播超時閾值
? ? ? ? final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT?
? ? ? ? ? ? ? ? : mBgConstants.TIMEOUT);
? ? ? ? startDeliveryTimeoutLocked(queue, softTimeoutMillis); // 啟動動態超時檢測
? ? } else {
? ? ? ? queue.setTimeoutScheduled(false); // 豁免超時檢測
? ? }
? ? // 處理后臺Activity啟動權限的超時(新增特性)
? ? if (r.mBackgroundStartPrivileges.allowsAny()) {
? ? ? ? final long bgStartTimeout = r.isForeground() ? mFgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT
? ? ? ? ? ? ? ? : mBgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT;
? ? ? ? // 通過Handler延遲發送超時消息
? ? ? ? mLocalHandler.sendMessageDelayed(
? ? ? ? ? ? Message.obtain(mLocalHandler, MSG_BG_ACTIVITY_START_TIMEOUT, args), bgStartTimeout);
? ? }
}
ActivityManagerService定義了廣播ANR的雙閾值動態控制:
前臺廣播(mFgConstants.TIMEOUT):默認10秒,保障用戶體驗。
后臺廣播(mBgConstants.TIMEOUT):默認60秒,放寬限制以節省資源
public class ActivityManagerService extends IActivityManager.Stub
? ? ? ? implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
? ? static final int BROADCAST_FG_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 前臺廣播默認10秒
? ? static final int BROADCAST_BG_TIMEOUT = 60 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 后臺廣播默認60秒
? ? public BroadcastQueue getBroadcastQueue(ActivityManagerService service) {
? ? ? ? // Broadcast policy parameters
? ? ? ? final BroadcastConstants foreConstants = new BroadcastConstants(
? ? ? ? ? ? ? ? Settings.Global.BROADCAST_FG_CONSTANTS);
? ? ? ? foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;
? ? ? ? final BroadcastConstants backConstants = new BroadcastConstants(
? ? ? ? ? ? ? ? Settings.Global.BROADCAST_BG_CONSTANTS);
? ? ? ? backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
? ? ? ? return new BroadcastQueueModernImpl(service, service.mHandler,
? ? ? ? ? ? ? ? ? ? foreConstants, backConstants);
? ? }
}
2. 目標進程接收并處理廣播
源碼路徑:frameworks/base/core/java/android/app/LoadedApk.ReceiverDispatcher.java
public void performReceive(Intent intent) {
? ? // 封裝為Runnable投遞到主線程消息隊列
? ? Args args = new Args(intent, ...);
? ? mActivityThread.post(args); // 使用主線程Handler
}
3.?超時檢測取消入口(finishReceiverLocked)
源碼路徑:frameworks/base/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
??????????????
BroadcastQueueModernImpl.finishReceiverLocked
/**
?* 處理廣播接收完成或超時的核心邏輯,更新狀態并觸發后續操作
?*?
?* @param queue 目標進程的廣播隊列(包含當前活躍的廣播記錄)
?* @param deliveryState 廣播投遞狀態(如超時/DELIVERY_TIMEOUT、正常完成等)
?* @param reason 狀態變更原因(用于日志和調試)
?*/
@GuardedBy("mService")
private void finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,
? ? ? ? @DeliveryState int deliveryState, @NonNull String reason) {
? ? // 1. 校驗隊列有效性:若當前無活躍廣播,直接返回
? ? if (!queue.isActive()) {
? ? ? ? logw("Ignoring finishReceiverActiveLocked; no active broadcast for " + queue);
? ? ? ? return;
? ? }
? ? // 2. 獲取當前廣播上下文
? ? final int cookie = traceBegin("finishReceiver"); // 性能追蹤標記
? ? final ProcessRecord app = queue.app; // 目標進程記錄
? ? final BroadcastRecord r = queue.getActive(); // 當前處理的廣播記錄
? ? final int index = queue.getActiveIndex(); // 接收者索引
? ? final Object receiver = r.receivers.get(index); // 具體的接收者對象
? ? // 3. 更新投遞狀態(如標記超時或完成)
? ? setDeliveryState(queue, app, r, index, receiver, deliveryState, reason);
? ? // 4. 處理超時場景
? ? if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
? ? ? ? r.anrCount++; // 記錄ANR次數
? ? ? ? if (app != null && !app.isDebugging()) { // 非調試進程才觸發ANR
? ? ? ? ? ? final AutoCloseable timer = mAnrTimer.accept(queue);
? ? ? ? ? ? final String packageName = getReceiverPackageName(receiver);
? ? ? ? ? ? final String className = getReceiverClassName(receiver);
? ? ? ? ? ? // 構造ANR報告記錄
? ? ? ? ? ? TimeoutRecord tr = TimeoutRecord.forBroadcastReceiver(r.intent, packageName,
? ? ? ? ? ? ? ? ? ? className).setExpiredTimer(timer);
? ? ? ? ? ? mService.appNotResponding(queue.app, tr); // 觸發ANR彈窗
? ? ? ? } else {
? ? ? ? ? ? mAnrTimer.discard(queue); // 調試進程忽略ANR
? ? ? ? }
? ? }?
? ? // 5. 正常完成時取消超時檢測
? ? else if (queue.timeoutScheduled()) {
? ? ? ? cancelDeliveryTimeoutLocked(queue); // 關鍵:移除超時消息
? ? }
? ? // 6. 檢查是否有等待條件被滿足(如有序廣播的鏈式觸發)
? ? checkAndRemoveWaitingFor();
? ? traceEnd(cookie); // 結束性能追蹤
}
1.廣播處理完成 → finishReceiverActiveLocked(DELIVERY_DONE, "正常完成")
→ cancelDeliveryTimeoutLocked()
├─ mAnrTimer.cancel()
└─ (舊版) Handler.removeMessages()
2.廣播超時 → finishReceiverActiveLocked(DELIVERY_TIMEOUT, "超時")
→ mService.appNotResponding()
→ 觸發ANR彈窗或日志記錄
4.觸發AMS的ANR彈窗
@Override
public void appNotResponding(@NonNull String processName, int uid,
? ? ? ? @NonNull TimeoutRecord timeoutRecord) {
? ? ActivityManagerService.this.appNotResponding(processName, uid, timeoutRecord);
}
廣播ANR中系統優化方案參考