從原始碼瞭解BroadcastReceiver的工作過程
這篇文章本來應該是繼續看 VirtualApk
中關於 外掛BroadcastReceiver
的處理的。不過由於處理邏輯比較簡單(在載入外掛的時候把外掛的所有 BroadcastReceiver
轉為動態廣播並註冊),所以這裡就不看了。
本文就從Android原始碼(8.0)來看一下系統對 BroadcastReceiver
的處理邏輯(廣播接收者註冊、傳送廣播), BroadcastReceiver
的原始碼處理邏輯很多也很複雜,我們只看重點,所以對於廣播一些很細緻的點是看不到了。本文的目標是瞭解系統對廣播的整個處理的過程。
BroadcastReceiver的註冊
動態註冊廣播接收者
我們從動態註冊開始看 : context.registerReceiver(mBroadcastReceiver, intentFilter)
, 最終呼叫的方法是 ContextImpl.registerReceiverInternal()
:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; ... rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver() ... ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags) }
即構造了一個 LoadedApk.ReceiverDispatcher
, 然後就轉到 ActivityManagerService
去註冊。那 LoadedApk.ReceiverDispatcher
是什麼呢?
LoadedApk
: 這個類是用來儲存當前執行app的狀態的類,它儲存著app的 Application
、類載入器、receiver、service等資訊。
LoadedApk.ReceiverDispatcher
: 這個類含有一個 InnerReceiver(Stub Binder)
,用來和服務端通訊,當 ActivityManagerService
分發廣播時,就會通過這個 (Stub)Binder
呼叫 BroadcastReceiver.onReceiver()
。這個我們到後續看廣播接收的時候再講。先知道這個類可以被 ActivityManagerService
用來和客戶端通訊即可。
ActivityManagerService.registerReceiver()
這個方法的註冊邏輯也比較簡單,這裡我們不看 粘性廣播(已被廢棄)
的註冊部分:
public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter ...) { ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) { rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver); if (rl.app != null) { ... rl.app.receivers.add(rl); } else { ... } mRegisteredReceivers.put(receiver.asBinder(), rl); } BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId, instantApp, visibleToInstantApps); rl.add(bf); mReceiverResolver.addFilter(bf); }
即把一個 BroadcastFilter
放入 ReceiverList
和 mReceiverResolver
中。那這兩個又是什麼呢?
BroadcastFilter
: 它是 IntentFilter
的子類,即一個 BroadcastReceiver
的 IntentFilter
,儲存一些 BroadcastReceiver
特有的一些資訊,比如許可權等。
ReceiverList
: 我們知道一個 BroadcastReceiver
可以有多個 BroadcastFilter(IntentFilter)
。它是用來儲存一個 BroadcastReceiver
的 BroadcastFilter
列表的。 mRegisteredReceivers
是一個儲存 ReceiverList
的map。它的key是一個 Binder
,即 LoadedApk.ReceiverDispatcher
中的 InnerReceiver(Stub Binder)
。value就是 ReceiverList
。 Binder
作為key是為了方便和 BroadcastReceiver
的客戶端通訊。
- mReceiverResolver
看一下它的型別 : IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
IntentResolver
這個類還是比較熟悉的,它可以解析一個 intent
。 我們知道可以使用 IntentFilter
來匹配一個 Intent
。 BroadcastFilter
就是來匹配 BroadcastReceiver
的 Intent
。 mReceiverResolver
裡面維護了一個 BroadcastFilter
列表。所以 mReceiverResolver
就是可以用來解析一個廣播的 Intent
。找出其匹配的 BroadcastReceiver
。
即註冊過程可以使用下圖表示:

BroadcastReceiver的註冊.png
即廣播的註冊過程就是把註冊的 BroadcastFilter(IntentFilter)
放到系統的 BroadcastFilter
維護列表( mRegisteredReceivers
和 mReceiverResolver
)中。目的是為了在接收廣播時好找到對應的廣播接收者
BroadcastReceiver的接收
在我們註冊了 BroadcastReceiver
之後,系統在收到廣播時,是如何正確的分發的呢?還是先找一個入口點,我們從傳送一個無序廣播 ContextImpl.sendBroadcast()
開始看:
public void sendBroadcast(Intent intent) { ... ActivityManager.getService().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, getUserId()); }
即直接轉發到了 ActivityManagerService.broadcastIntentLocked()
, 這個方法很長,我們去除對於系統特殊廣播和粘性廣播接收邏輯的處理來看:
int broadcastIntentLocked(...Intent intent, String resolvedType, IIntentReceiver resultTo, ...,boolean ordered,...){ ... 對於特定系統廣播的分發處理 以及 粘性廣播的處理 List receivers = null; // manifest註冊的廣播 List<BroadcastFilter> registeredReceivers = null; // 動態註冊 //動態註冊的廣播接收者也可以接收這個廣播 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { receivers = collectReceiverComponents(intent, resolvedType, callingUid, users); //收集在Manifest中註冊的可以接收這個intent的廣播接收者 } //沒有顯示指明廣播接收者 if (intent.getComponent() == null) { if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) { ...對於全部使用者符合條件的廣播接收者的收集 }else{ //收集當前的符合條件的廣播接收者。 mReceiverResolver儲存著動態註冊的廣播資訊 registeredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false /*defaultOnly*/, userId); } } //處理程式碼動態註冊的廣播 int NR = registeredReceivers != null ? registeredReceivers.size() : 0; if (!ordered && NR > 0) { ... //這個queue的作用是把廣播分發給廣播接收者 final BroadcastQueue queue = broadcastQueueForIntent(intent); //利用 registeredReceivers 構建一個 BroadcastRecord BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); ... queue.enqueueParallelBroadcastLocked(r); //併發分發廣播到廣播接收者 queue.scheduleBroadcastsLocked(); } ... 根據廣播接收者的 priority 調整 receivers中廣播接收者的順序 //處理manifest靜態註冊的廣播 if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); .... queue.enqueueOrderedBroadcastLocked(r); //序列分發廣播到廣播接收者 queue.scheduleBroadcastsLocked(); } }
大體邏輯我就不解釋了,上面程式碼還是加了挺詳細的註釋的。可以看到最終對於廣播的分發過程是:
- 根據一個廣播的
Intent
獲取對應的BroadcastQueue
- 根據一個廣播接收者列表建立一個
BroadcastRecord
- 把
BroadcastRecord
新增到BroadcastQueue
中 -
BroadcastQueue
開始分發廣播給廣播接收者queue.scheduleBroadcastsLocked()
- BroadcastQueue
原始碼中一共存在兩個 BroadcastQueue
, 一個是前臺廣播佇列( mFgBroadcastQueue
),一個是後臺廣播佇列( mBgBroadcastQueue
)。這兩廣播佇列最直接的區別是 mFgBroadcastQueue
在分發廣播時超時時間為 10s
, mBgBroadcastQueue
在分發廣播時超時時間是 60s
。
所以我們繼續看 BroadcastQueue
是如何把廣播分發給廣播接收者的 queue.scheduleBroadcastsLocked()
, 這個方法最終呼叫的是 BroadcastQueue.processNextBroadcastLocked()
,這個方法程式碼也很長,分成兩個部分來看:
無序廣播的分發
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { BroadcastRecord r; while (mParallelBroadcasts.size() > 0) {//處理廣播接收者並行集合 r = mParallelBroadcasts.remove(0); // 獲得一個`BroadcastRecord` .... final int N = r.receivers.size(); for (int i=0; i<N; i++) { //分發廣播給 廣播接收者 deliverToRegisteredReceiverLocked(r, (BroadcastFilter)r.receivers.get(i), false, i); } ... } ... }
邏輯很簡單,即從 mParallelBroadcasts
取出一個 BroadcastRecord
然後呼叫 deliverToRegisteredReceiverLocked
去分發,它裡面的邏輯大部分都是許可權判斷和對無序廣播跳過,因此不看它的具體內容了,它最終會呼叫到 performReceiveLocked
:
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, ...) throws RemoteException { if (app != null) { if (app.thread != null) { app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState); } } else { receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); } }
即如果廣播接收者所在的主執行緒不為null,即直接通過 ApplicationThread(Binder)
切換到這個程序的主執行緒去接收這個廣播,否則通過 IIntentReceiver(Binder)
切換到對應的程序去接收廣播。最終的結果都是例項化一個 BroadcastReceiver
,在主執行緒呼叫其 onReceiver
方法。這兩條路徑最終都會走到一個地方,然後呼叫下面程式碼(其實有序廣播最終也是來到這個地方):
//執行在主執行緒 ClassLoader cl = mReceiver.getClass().getClassLoader(); intent.setExtrasClassLoader(cl); intent.prepareToEnterProcess(); setExtrasClassLoader(cl); receiver.setPendingResult(this); receiver.onReceive(mContext, intent); ... finish();//
即例項化 BroadcastReceiver
,呼叫 onReceive()
。 最後呼叫 finish()
。這個方法很關鍵,因為它負責再告訴 ActivityManagerService
,這個廣播處理完畢了:
if (mOrderedHint) { am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,mAbortBroadcast, mFlags); } else { am.finishReceiver(mToken, 0, null, null, false, mFlags); }
所以我們再回到 ActivityManagerService
來看一下:
public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, int flags) { BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0 ? mFgBroadcastQueue : mBgBroadcastQueue; r = queue.getMatchingOrderedReceiver(who); if (r != null) { doNext = r.queue.finishReceiverLocked(r, resultCode, resultData, resultExtras, resultAbort, true); } if (doNext) { r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); } .... }
通過客戶端傳過來的引數,可以看出這個方法其實只是對 有序廣播
做了處理,對無序廣播並沒有做處理。也可以猜出,對有序廣播處理的原因是要保證接下來廣播可以繼續處理。好到這裡,無序廣播的分發流程就看完了。
有序廣播的分發
按照我們前面看的廣播註冊的原始碼,有序廣播是指指定了廣播的 priority
屬性。 BroadcastQueue.mOrderedBroadcasts
會把 BroadcastRecord
按照這個順序依次排列。因此處理有序廣播其實就是把 mOrderedBroadcasts
的 BroadcastRecord
拿出來一個一個的處理。這裡還是從 BroadcastQueue.processNextBroadcastLocked()
一點一點的來看 :
BroadcastRecord r; //確定要分發的有序廣播,如果在遍歷過程中發現了超時的廣播,則直接強制分發 do { ... r = mOrderedBroadcasts.get(0); //超時會強制分發廣播 forceReceive = true if (... || forceReceive) { ... performReceiveLocked(r.callerApp, r.resultTo,new Intent(r.intent), r.resultCode,...); ... r = null; continue; } } while (r == null); // nextReceiver 其實就是一個index, 我們知道 BroadcastRecord 是有一個廣播接收者列表的 int recIdx = r.nextReceiver++; final Object nextReceiver = r.receivers.get(recIdx); //拿出一個廣播接收者 if (nextReceiver instanceof BroadcastFilter) { //處理動態註冊的廣播接收者 BroadcastFilter filter = (BroadcastFilter)nextReceiver; //分發這個廣播給這個廣播接收者 deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx); return; //前面看無序廣播的時候已經知道,要接收到前一個廣播接收者接收完成的訊號才會繼續分發有序廣播 } ResolveInfo info = (ResolveInfo)nextReceiver; //靜態註冊的廣播接收者 ...一系列的許可權判斷,如果有問題直接跳過 //廣播接收者所在的程序正在執行 if (app != null && app.thread != null && !app.killed) { .... processCurBroadcastLocked(r, app, skipOomAdj); ... return; } //嘗試喚醒廣播接收者所在的程序 if ((r.curApp=mService.startProcessLocked(targetProcess...) == null) { ..喚起失敗 scheduleBroadcastsLocked(); //直接處理下一個 return; } //程序喚起成功,把廣播設定為 pending mPendingBroadcast = r; mPendingBroadcastRecvIndex = recIdx;
其實上面的註釋我已經寫的挺清楚的了。所以這裡不做過多的介紹。 deliverToRegisteredReceiverLocked()
這個方法就是前面分發無序廣播的方法。所以不再看了,我們看一下 processCurBroadcastLocked()
:
private final void processCurBroadcastLocked(BroadcastRecord r, ProcessRecord app, boolean skipOomAdj) throws RemoteException { ... app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId, app.repProcState); ... }
很簡單,即還是回到了廣播的程序去例項化廣播,呼叫其 onReceive
方法。到這裡可以知道: 有序廣播和無序廣播在客戶端的處理是一樣的 。那一個有序廣播客戶端處理完畢之後怎麼辦呢? 前面在看無序廣播的時候已經知道會
再次回到 ActivityManagerService
,呼叫 finishReceiver()
方法。這個方法我們前面已經貼過了,不過我們再把它的主要邏輯貼出來:
BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0 ? mFgBroadcastQueue : mBgBroadcastQueue; r = queue.getMatchingOrderedReceiver(who); if (r != null) { doNext = r.queue.finishReceiverLocked(r, resultCode,resultData, resultExtras, resultAbort, true); } if (doNext) { r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); }
processNextBroadcastLocked()
這個方法是分發廣播的入口,我們不再看了。看一下 r.queue.finishReceiverLocked()
:
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) { ...清除狀態 if (r.nextReceiver < r.receivers.size()) { Object obj = r.receivers.get(r.nextReceiver); nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null; } else { nextReceiver = null; } .... }
即清除了一些狀態,然後確定了這個 BroadcastRecord
的下一個 BroadcastReceiver
。後續會繼續分發廣播給這個 BroadcastReceiver
。
即有序廣播的分發通過上面的機制會依次分發給廣播接收者 。
看完一遍原始碼,弄的雲裡霧裡的,因此使用下面這張圖來理清整個系統的廣播處理機制:

Android廣播接收者處理邏輯.png
LocalBroadcastManager
平時如果我們只是在app內使用廣播來做簡單的通知等,可以使用它來註冊廣播接收者和傳送廣播。它會自己管理註冊的廣播接受者(不會管理靜態註冊的廣播),然後做正常的分發,完全不涉及 ActivityManagerService
。因此比較高效。原始碼比較簡單就不做分析。
歡迎關注我的 ofollow,noindex">Android進階計劃 看更多幹貨。