BroadCastReceiver的工作過程分為包含兩方面的內(nèi)容:
- 注冊
- 發(fā)送和接收
使用BroadcastReceiver很簡單,只需要繼承BroadcastReceiver并重寫它的onReceive方法即可咬崔。
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//TODO something
}
}
為了使BroadcastReceiver工作奢浑,就需要對它進(jìn)程注冊法焰。注冊BroadCastReceiver分為在AndroidManifest.xml靜態(tài)注冊和代碼中動態(tài)注冊氧卧。
靜態(tài)注冊示例:
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.sososeen.09.demo.my.receiver"/>
</intent-filter>
</receiver>
動態(tài)注冊也很簡單:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.sososeen.09.demo.my.receiver");
registerReceiver(new MyReceiver(), intentFilter);
不過動態(tài)注冊別忘了反注冊亚亲,否則會造成內(nèi)存泄漏救拉。
發(fā)送廣播示例如下难审,通過給Intent設(shè)置action,對應(yīng)的廣播接收者的onReveive就會被回調(diào)了亿絮。
Intent intent = new Intent();
intent.setAction("com.sososeen.09.demo.my.receiver");
sendBroadcast(intent);
注冊廣播和發(fā)送廣播中間過程是怎么進(jìn)行的告喊,我們今天就來分析一下麸拄。由于靜態(tài)注冊牽涉到在應(yīng)用安裝時(shí)有系統(tǒng)自動完成注冊,確切的說是通過PackageManagerService完成注冊黔姜,相對比較復(fù)雜拢切。今天先來分析一下動態(tài)注冊的BroadCastReceiver的工作過程。
在本文的分析中秆吵,ActivityManagerService簡稱AMS淮椰。
動態(tài)注冊BroadCastReceiver
注冊廣播接收者是通過ContextWrapper來進(jìn)行的,但是類似啟動Service纳寂,真正干事的還是ContextImpl主穗,我們直接來看它的方法。2個參數(shù)的registerReceiver方法毙芜,會調(diào)用4個參數(shù)的registerReceiver方法忽媒,然后會調(diào)用registerReceiverInternal方法。
# android.app.ContextImpl
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext());
}
在registerReceiverInternal方法中主要干了兩件事:
mPackageInfo指的是LoadedApk對象腋粥,通過調(diào)用LoadedApk的getReceiverDispatcher方法把BroadcastReceiver包裝為ReceiverDispatcher.InnerReceiver對象晦雨,這是因?yàn)锽roadcastReceiver是不具有跨進(jìn)程通信能力的,想要最終調(diào)用到BroadcastReceiver的onReceive方法灯抛,必須有一個Binder對象用來進(jìn)行IPC金赦,在這里就是ReceiverDispatcher.InnerReceiver對象。InnerReceiver對象持有ReceiverDispatcher對象的引用对嚼。當(dāng)AMS端調(diào)用InnerReceiver的接收廣播的方法夹抗,InnerReceiver對象會找到對應(yīng)的ReceiverDispatcher進(jìn)而找到BroadcastReceiver對象并調(diào)用其onReceive方法。這個過程我們后面再分析纵竖。這種方式與bind方式啟動Service的中包裝ServiceConnection是類似的漠烧,有興趣的可以看一下Android四大組件——Service的工作過程分析。
ActivityManagerNative.getDefault()獲取的是AMS在本地的代理靡砌,該代理通過IPC過程調(diào)用AMS的registerReceiver方法已脓。
# android.app.ContextImpl
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
// 1 創(chuàng)建ReceiverDispatcher.InnerReceiver對象
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();
}
}
try {
// 2 IPC過程調(diào)用AMS的registerReceiver方法
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
我們先來看一下LoadedApk的getReceiverDispatcher方法,就是創(chuàng)建一個ReceiverDispatcher對象并將其保存起來通殃,在ReceiverDispatcher的構(gòu)造方法中創(chuàng)建InnerReceiver對象度液,ReceiverDispatcher對象中保存了BroadcastReceiver和InnerReceiver對象。
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();
}
}
我們來看一下AMS的registerReceiver方法画舌,該方法很長堕担,我們選取一些關(guān)鍵的地方。
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
...
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);
}
...
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
...
mReceiverResolver.addFilter(bf);
...
}
}
在上面的方法中
mRegisteredReceivers 是一個HashMap對象曲聂,用于追蹤客戶端BroadcastReceiver對應(yīng)的ReceiverDispatcher.InnerReceiver對象霹购。ReceiverList是一個ArrayList集合,用于存放BroadcastFilter朋腋,BroadcastFilter用于包裝IntentFilter齐疙。我們需要知道一點(diǎn)膜楷,同一個BroadcastReceiver是可以對應(yīng)多個IntentFilter的,只要匹配一個就能夠接收消息贞奋。
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
說明: BroadcastFilter中封裝了ReceiverList對象赌厅,而ReceiverList對象中包含了客戶端BroadcastReceiver對應(yīng)的IIntentReceiver對象(實(shí)際上就是ReceiverDispatcher.InnerReceiver對象),將來在發(fā)送廣播的階段忆矛,通過Intent找到所有匹配到的動態(tài)注冊的BroadcastFilter集合察蹲。
到此,BroadcastReceiver的動態(tài)注冊過程就完畢了催训。
廣播的發(fā)送和接收過程
廣播的發(fā)送分為普通廣播洽议、有序廣播和粘性廣播。有序廣播是按照廣播接收者的優(yōu)先級漫拭,從高優(yōu)先級到低優(yōu)先級依次接收亚兄,而且在高優(yōu)先級的廣播接收者中是可以打斷廣播,造成低優(yōu)先級的廣播接收不到采驻。而粘性廣播表示想要當(dāng)發(fā)送廣播的時(shí)候审胚,BroadcastReceiver還沒有創(chuàng)建,當(dāng)BroadcastReceiver創(chuàng)建后礼旅,它的onReveive方法會被立即調(diào)用膳叨。
在這里我們分析一下普通廣播的發(fā)送和接收流程。
調(diào)用Context的方法發(fā)送廣播痘系,實(shí)際上還是會調(diào)用ContextImpl的方法菲嘴,來一下ContextImpl的sendBroadcast方法
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().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();
}
}
從這個過程中看,通過一個IPC過程調(diào)用到AMS的broadcastIntent方法汰翠,在該方法又調(diào)用了AMS的broadcastIntentLocked方法龄坪。
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
broadcastIntentLocked方法非常長,我們盡量選取一些關(guān)鍵的地方來分析复唤,在方法的開始會有如下代碼:
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
表示會重新創(chuàng)建一個Intent對象用于包裹傳遞過來的的Intent對象健田,并且為Intent添加了一個flag,Intent.FLAG_EXCLUDE_STOPPED_PACKAGES佛纫,表示默認(rèn)情況下廣播不會發(fā)送給已經(jīng)停止運(yùn)行的App妓局,這也是為了方式一些App想要利用廣播來啟動進(jìn)程。
從Android3.1開始呈宇,添加了兩個標(biāo)記
// 表示不包含已經(jīng)停止的應(yīng)用跟磨,如果廣播設(shè)置這個標(biāo)記,就不會發(fā)送給已經(jīng)停止的應(yīng)用
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
// 表示包含已經(jīng)停止的應(yīng)用攒盈,如果廣播設(shè)置這個標(biāo)記,就會發(fā)送給已經(jīng)停止的應(yīng)用
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
從Android 3.1開始哎榴,默認(rèn)情況下都是添加一個FLAG_EXCLUDE_STOPPED_PACKAGES標(biāo)記型豁,表示不會發(fā)送給停止的應(yīng)用僵蛛,如果確實(shí)需要的話,需要給廣播顯式的設(shè)置FLAG_INCLUDE_STOPPED_PACKAGES標(biāo)記迎变。
在broadcastIntentLocked內(nèi)部充尉,會根據(jù)IntentFilter查找出匹配的BroadcastReceiver,經(jīng)過一系列條件的篩選和過濾衣形,將滿足條件的接收者放在BroadcastQueue中驼侠,接下來BroadcastQueue就會將廣播發(fā)送出去。注意在這個過程中會先把找出來的動態(tài)注冊的廣播發(fā)出去谆吴,然后再把靜態(tài)注冊的廣播發(fā)出去倒源。因此,動態(tài)注冊的廣播優(yōu)先級是高于靜態(tài)注冊的廣播的句狼。而且笋熬,如果BroadcastReceiver同時(shí)注冊了靜態(tài)廣播和動態(tài)廣播,其onReceive方法會被調(diào)用兩次腻菇。
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Enqueueing broadcast " + r.intent.getAction());
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
BroadcastQueue發(fā)送廣播的實(shí)現(xiàn)如下胳螟,通過Handler發(fā)送一個BROADCAST_INTENT_MSG類型的消息,Handler收到消息之后會調(diào)用BroadcastQueue的processNextBroadcast方法筹吐。
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;
}
BroadcastQueue的processNextBroadcast方法很長糖耸,我們選取發(fā)送普通廣播的部分來看,可以看到無序廣播存放在mParallelBroadcasts集合中丘薛,通過遍歷這個集合來發(fā)送廣播給BroadcastReceiver嘉竟。具體的發(fā)送過程是deliverToRegisteredReceiverLocked方法。
final void processNextBroadcast(boolean fromMsg) {
...
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
}
再來看一下BroadcastQueue的deliverToRegisteredReceiverLocked方法榔袋,該方法負(fù)責(zé)將一個特定的廣播發(fā)送給特定的接收者周拐,具體的發(fā)送過程是調(diào)用了performReceiveLocked方法。
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
來看一下performReceiveLocked方法凰兑,app.thread指的就是ApplicationThread在本地的代理對象妥粟,app.thread != null 條件滿足,會通過IPC過程調(diào)用ApplicationThread的scheduleRegisteredReceiver方法吏够。
# com.android.server.am.BroadcastQueue
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 {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
...
} catch (RemoteException 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);
}
}
在ApplicationThread的scheduleRegisteredReceiver方法中調(diào)用了receiver的performReceive方法勾给,而這個receiver我們知道,實(shí)際上就是ReceiverDispatcher.InnerReceiver對象锅知。
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);
}
來看一下ReceiverDispatcher.InnerReceiver對象的performReceive方法播急,在該方法中首先獲取InnerReceiver持有的ReceiverDispatcher對象,如果ReceiverDispatcher對象不為空的話就會調(diào)用它的performReceive方法售睹。
# android.app.LoadedApk.ReceiverDispatcher.InnerReceiver
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
if (intent == null) {
Log.wtf(TAG, "Null intent received");
rd = null;
} else {
rd = mDispatcher.get();
}
...
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
...
}
}
在ReceiverDispatcher的performReceive方法中桩警,會封裝一個Args對象,Args是ReceiverDispatcher的非靜態(tài)內(nèi)部類昌妹,因此持有ReceiverDispatcher的引用捶枢,可以訪問ReceiverDispatcher的參數(shù)和方法握截,并且它實(shí)現(xiàn)了Runnable接口。mActivityThread是一個Handler烂叔,通過post方法谨胞,把調(diào)用切換到主線程中來,Args的run方法會被執(zhí)行蒜鸡。
# android.app.LoadedApk.ReceiverDispatcher
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (intent == null) {
Log.wtf(TAG, "Null intent received");
} else {
...
}
if (intent == null || !mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
}
Args的run方法如下胯努,最后會調(diào)用BroadcastReceiver的onReceive方法。
# android.app.LoadedApk.ReceiverDispatcher.Args
public void run() {
final BroadcastReceiver receiver = mReceiver;
...
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
...
}
到此逢防,廣播的發(fā)送和接收過程也分析完畢了叶沛。
總結(jié)
注冊廣播的時(shí)候是把BroadcastReceiver封裝到ReceiverDispatcher中,并且把與此對應(yīng)的ReceiverDispatcher.InnerReceiver作為一個Binder對象傳遞到AMS端胞四,AMS端會通過一個Map集合來存放ReceiverDispatcher.InnerReceiver對象和對應(yīng)的IntentFilter包裝類恬汁。
發(fā)送廣播的時(shí)候,通過一個IPC過程調(diào)用到AMS端辜伟,AMS端會找到匹配的廣播接收者并添加到BroadcastQueue中氓侧,在BroadcastQueue中進(jìn)程處理之后通過一個IPC過程調(diào)用到ApplicationThread的scheduleRegisteredReceiver方法,然后會調(diào)用ReceiverDispatcher.InnerReceiver的方法导狡,再經(jīng)過ReceiverDispatcher约巷、以及它的內(nèi)部類Args,最終調(diào)用到BroadcastReceiver的onReveive方法旱捧。
動態(tài)注冊的廣播優(yōu)先級是高于靜態(tài)注冊的廣播的独郎。而且,如果BroadcastReceiver同時(shí)注冊了靜態(tài)廣播和動態(tài)廣播枚赡,其onReceive方法會被調(diào)用兩次氓癌。
從過程中也可以看到,AMS端如果想要與客戶端打交道的話都是通過ApplicationThread贫橙。