Android四大元件——BroadcastReceiver(原理篇)
前言
Android四大元件——BroadcastReceiver(基礎篇)裡面介紹了BroadcastReceiver相關的基礎知識,本文將從Android 8.0原始碼來分析一下廣播的註冊和接收原理。
BroadcastReceiver的註冊
Android系統中BroadcastReceiver的註冊方式分為動態註冊和靜態註冊兩種。動態註冊必須在程式執行期動態註冊,其實際的註冊動作由ContextImpl物件完成;靜態註冊則是在AndroidManifest.xml中宣告的。在基礎篇中提到過,因為靜態註冊耗電、佔記憶體、不受程式生命週期影響,所以Google在Android 8.0上禁止了大部分廣播的靜態註冊,以此來減少耗電、增加待機時間、節省記憶體空間、提升效能。
動態註冊過程原始碼分析
Activity是通過Context類的registerReceiver()方法進行動態註冊廣播監聽的。Context是一個抽象類,它是應用端和AMS、WMS等系統服務進行通訊的介面,Activity、Service和Application都是繼承它的子類。Context的實現類是ContextImpl,也就是說註冊時最終呼叫到的是ContextImpl中的registerReceiver方法。下面將以registerReceiver為入口一步步分析廣播是如何動態註冊和接收的。
ContextImpl中的registerReceiver方法最終會呼叫本類的私有方法registerReceiverInternal。在這個方法裡面主要做了兩件事情,一件是通過LoadedApk類提供的方法獲得IIntentReceiver的例項,另一件是通過ActivityManagerService.registerReceiver方法把廣播註冊到AMS中。
/** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */ class ContextImpl extends Context { ... private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { //1、通過LoadedApk類提供的方法獲得IIntentReceiver的例項 IIntentReceiver rd = null; if (receiver != null) { if (mPackageInfo != null && context != null) { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { if (scheduler == null) { scheduler = mMainThread.getHandler(); } rd = new LoadedApk.ReceiverDispatcher( receiver, context, scheduler, null, true).getIIntentReceiver(); } } //2、通過ActivityManagerService.registerReceiver方法把廣播註冊到AMS中 try { final Intent intent = ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags); if (intent != null) { intent.setExtrasClassLoader(getClassLoader()); intent.prepareToEnterProcess(); } return intent; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } ... }
接下來跳轉到LoadedApk和ActivityManagerService中看看在IIntentReceiver例項化和廣播註冊到AMS的時候具體做了什麼事情。
- 客戶端(LoadedApk)
當Activity中有新的BroadcastReceiver被註冊,LoadedApk就會為他生成一個ReceiverDispatcher例項,然後把Context、BroadcastReceiver和ReceiverDispatcher三者的關係儲存到關係對映表中。其中,在ReceiverDispatcher的構造方法中生成了IIntentReceiver類的例項,並可以通過ReceiverDispatcher.getIIntentReceiver方法獲得。
LoadedApk的相關原始碼如下:
/**
* Local state maintained about a currently loaded .apk.
* @hide
*/
public final class LoadedApk {
...
//3、Context、BroadcastReceiver和ReceiverDispatcher三者對映關係表
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<>();
...
//4、把Context、BroadcastReceiver和ReceiverDispatcher三者對映關係儲存到對映表中
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
...
static final class ReceiverDispatcher {
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
if (activityThread == null) {
throw new NullPointerException("Handler must not be null");
}
//5、例項化IIntentReceiver
mIIntentReceiver = new InnerReceiver(this, !registered);
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
mInstrumentation = instrumentation;
mRegistered = registered;
mLocation = new IntentReceiverLeaked(null);
mLocation.fillInStackTrace();
}
}
...
//6、獲取IIntentReceiver類的例項
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
...
}
- 服務端(ActivityManagerService)
AMS在收到客戶端廣播註冊請求時,會把提供服務的IIntentReceivers介面、ReceiverList和BroadcastFilter的對映關係儲存到對映關係表中。同時,把BroadcastFilter儲存到廣播解析器IntentResolver中。
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
...
//7、提供服務的IBinder介面、ReceiverList和BroadcastFilter的對映關係表
/**
* Keeps track of all IIntentReceivers that have been registered for broadcasts.
* Hash keys are the receiver IBinder, hash value is a ReceiverList.
*/
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
//8、儲存BroadcastFilter的廣播解析器
/**
* Resolver for broadcast intents to registered receivers.
* Holds BroadcastFilter (subclass of IntentFilter).
*/
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
= new IntentResolver<BroadcastFilter, BroadcastFilter>() {...};
...
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
...
synchronized (this) {
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// Original caller already died
return null;
}
//9、把IBinder、ReceiverList和BroadcastFilter的對映關係儲存到對映關係表中
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 {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
throw new IllegalArgumentException(
"Receiver requested to register for uid " + callingUid
+ " was previously registered for uid " + rl.uid
+ " callerPackage is " + callerPackage);
} else if (rl.pid != callingPid) {
throw new IllegalArgumentException(
"Receiver requested to register for pid " + callingPid
+ " was previously registered for pid " + rl.pid
+ " callerPackage is " + callerPackage);
} else if (rl.userId != userId) {
throw new IllegalArgumentException(
"Receiver requested to register for user " + userId
+ " was previously registered for user " + rl.userId
+ " callerPackage is " + callerPackage);
}
//10、把BroadcastFilter儲存到對應的ReceiverList中,BroadcastFilter裡面包含了IntentFilter和ReceiverList等相關資訊
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
//11、把BroadcastFilter儲存到廣播解析器IntentResolver中
mReceiverResolver.addFilter(bf);
...
}
}
...
}
- 註冊小結
每一個應用都持有一個LoadedApk例項,LoadedApk例項中包含多個Context例項(一個程序對應多個Activity和Service以及一個Application),每個Context例項可能建立了多個BroadcastReceiver例項,每個BroadcastReceiver例項在動態註冊的時候都會生成一個對應的ReceiverDispatcher例項,每個ReceiverDispatcher例項內部又會由InnerReceiver類生成一個IIntentReceiver例項。這個IIntentReceiver例項在動態註冊BroadcastReceiver的時候會被傳遞給AMS,AMS會為每個IIntentReceiver例項建立一個ReceiverList例項,每個ReceiverList例項中儲存了多個BroadcastFilter例項,而這個BroadcastFilter例項裡面包含了具體的IntentFilter和ReceiverList等相關資訊。
靜態註冊過程原始碼分析
BroadcastReceiver靜態註冊指的是在AndroidManifest.xml中宣告的接收器,在系統啟動的時候,會由PMS去解析。當AMS呼叫PMS的介面來查詢廣播註冊的時候,PMS會查詢記錄並且返回給AMS。
廣播的傳送和接收
以最簡單普通廣播為例,直接跳到Context的實現類ContextImpl的sendBroadcast方法。從原始碼看sendBroadcast裡面基本沒幹什麼事,直接去呼叫的AMS的broadcastIntent方法。
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
// 1、呼叫的AMS的broadcastIntent方法傳送廣播
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
AMS中的broadcastIntent方法裡面也沒幹什麼,直接呼叫本類的broadcastIntentLocked方法(broadcastIntentLocked方法有600多行程式碼,OMG)。
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
// Figure out who all will receive this broadcast.
List receivers = null;
// 2、註冊的BroadcastFilter列表,BroadcastFilter裡面包含了IntentFilter和ReceiverList等相關資訊
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
//3、根據intent從廣播解析器mReceiverResolver中查詢符合的BroadcastFilter列表
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, users[i]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
//4、根據intent從廣播解析器mReceiverResolver中查詢符合的BroadcastFilter列表
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
//5、封裝成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);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
// Note: We assume resultTo is null for non-ordered broadcasts.
if (!replaced) {
//6、把BroadcastRecord類例項新增到併發的廣播序列中並準備傳送
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
}
接下來看一下BroadcastRecord類中的scheduleBroadcastsLocked方法是如何把廣播發送到對應的BroadcastReceiver中的。
/**
* BROADCASTS
*
* We keep two broadcast queues and associated bookkeeping, one for those at
* foreground priority, and one for normal (background-priority) broadcasts.
*/
public final class BroadcastQueue {
...
//7、scheduleBroadcastsLocked方法傳送BROADCAST_INTENT_MSG訊息給handler
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
...
private final class BroadcastHandler extends Handler {
public BroadcastHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
//8、handler執行processNextBroadcast方法開始傳送廣播
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
}
...
final void processNextBroadcast(boolean fromMsg) {
...
do {
...
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// No more receivers for this broadcast! Send the final
// result if requested...
if (r.resultTo != null) {
try {
//9、逐個傳送BroadcastRecord中的廣播
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {
r.resultTo = null;
}
}
...
}
} while (r == null);
...
}
...
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
//10、由ActivityThread中的scheduleRegisteredReceiver方法傳送給相應的的BroadcastReceiver
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
// DeadObjectException when the process isn't actually dead.
//} catch (DeadObjectException ex) {
// Failed to call into the process. It's dying so just let it die and move on.
// throw ex;
} catch (RemoteException ex) {
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ " (pid " + app.pid + "). Crashing it.");
app.scheduleCrash("can't deliver broadcast");
}
throw ex;
}
} else {
// Application has died. Receiver doesn't exist.
throw new RemoteException("app.thread must not be null");
}
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
...
}
在ActivityThread的scheduleRegisteredReceiver方法中執行了IIntentReceiver.performReceive方法。
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
逐個IIntentReceiver介面的實現是在LoadedApk中的,回去呼叫LoadedApk的performReceive方法,最終調到Args.getRunnable方法。
public final Runnable getRunnable() {
...
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
//11、回撥BroadcastReceiver.onReceive方法
receiver.onReceive(mContext, intent);
} catch (Exception e) {
if (mRegistered && ordered) {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing failed broadcast to " + mReceiver);
sendFinished(mgr);
}
if (mInstrumentation == null ||
!mInstrumentation.onException(mReceiver, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Error receiving broadcast " + intent
+ " in " + mReceiver, e);
}
}
...
};
- 傳送和接收小結
應用端呼叫系統服務(AMS)傳送廣播,AMS會去廣播解析器IntentResolver中查詢哪些BroadcastFilter跟這個廣播有關聯,然後把相關資訊封裝成 BroadcastRecord類的例項新增到廣播發送序列BroadcastQueue中逐個廣播。在BroadcastQueue中廣播的時候會從BroadcastRecord中獲得BroadcastFilter進而獲得對應的ReceiverList,ReceiverList中包含了對應的IIntentReceiver例項,通過這個IIntentReceiver例項就可以找到對應的BroadcastReceiver,呼叫其BroadcastReceiver.OnReceive方法把廣播傳遞給對應的BroadcastReceiver。
總結
通過上面的分析,我們深入瞭解了廣播接收器BroadcastReceiver是如何完成動態註冊的,以及廣播是如何傳送和被接收的。中間涉及到諸多個類的多個方法調來調去,看起來比較複雜,但是如果你看懂了之前BroadcastReceiver動態註冊的關係圖,理解起來就相對簡單了。
廣播接收器BroadcastReceiver的動態註冊過程可以簡單的理解為建立兩個對映關係的過程:
- 建立LoadedApk、Context、BroadcastReceiver和ReceiverDispatcher的對映關係。
- 建立ReceiverList和BroadcastFilter的對映關係。
這兩個對映關係共同持有同一個IIntentReceiver,IIntentReceiver是這兩個對映關係中間的橋樑,客戶端和服務端通過IIntentReceiver介面來完成程序間通訊。
知道了上面的兩個對映關係以及這兩個對映關係之間的關聯之處,廣播的傳送和接收就可以簡單的理解為一個反向關係查詢的過程。AMS根據Context廣播的Intent資訊對照對映關係表從BroadcastFilter開始,反向一層一層找到與之對應的BroadcastReceiver,最終完成BroadcastReceiver.OnReceive的呼叫,把Intent傳遞給對應的BroadcastReceiver。
參考文獻
《Android開發藝術探索》——9.4BroadcastReceiver的工作過程
BroadcastReceiver原始碼解析(一)
BroadcastReceiver原始碼解析(二)