
一 BroadcastReceiver 簡(jiǎn)介
BroadcastReceiver 是一種消息型組件,稱之為廣播,用于在不同的組件乃至不同的應(yīng)用之間傳遞消息。
廣播的注冊(cè)分為兩種方式:
- 靜態(tài)注冊(cè)
靜態(tài)注冊(cè)是指在 AndroidManiifest中注冊(cè)廣播,這種廣播在應(yīng)用安裝時(shí)會(huì)被系統(tǒng)解析、不需要應(yīng)用啟動(dòng)就可以收到相應(yīng)的廣播. - 動(dòng)態(tài)注冊(cè)。
動(dòng)態(tài)注冊(cè)廣播需要通過(guò)Context.registrerReceiver() 來(lái)實(shí)現(xiàn),并且在不需要的時(shí)候要通過(guò)Context.unregistrerReceiver() 來(lái)解除廣播,此種形態(tài)的廣播必須要應(yīng)用啟動(dòng)才能注冊(cè)并接收廣播。
廣播收發(fā)器組件可以用來(lái)實(shí)現(xiàn)低耦合的觀察者模式,觀察者和被觀察者之間可以沒(méi)有任何耦合,由于BroadcastReceiver的特性,它不適合用來(lái)執(zhí)行耗時(shí)操作.BroadcastReceiver一般來(lái)說(shuō)不需要停止,它也沒(méi)有停止的概念。
二 BroadcastReceiver 的使用
我們回顧一下廣播的使用方法,首先要冊(cè)
定義廣播接收者,只需要繼承 BroadcastReceiver并重寫 on Receive方法即可,下面是一個(gè)典型的廣播接收者的實(shí)現(xiàn):
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// onReceive函數(shù)不能做耗時(shí)的事情,參考值:10s以內(nèi)
String action= intent. getAction ();
// do something
}
}
定義好了廣播接收者,接著還需要注冊(cè)廣播接收者,注冊(cè)分為兩種方式,既可以在AndroidManifest文件中靜態(tài)注冊(cè),也可以通過(guò)代碼動(dòng)態(tài)注冊(cè)。
靜態(tài)注冊(cè)的示例如下
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.lsh.receiver.LAUNCH"/>
</intent-filter>
</receiver>
通過(guò)代碼來(lái)動(dòng)態(tài)注冊(cè)廣播也是很簡(jiǎn)單的,如下所示。需要注意的是,動(dòng)態(tài)注冊(cè)
需要在合適的時(shí)機(jī)進(jìn)行解注冊(cè),解注冊(cè)采用 unregisterReceiver方法。
IntentFilter filter = new IntentFilter();
filter.addAction("com.lsh.receiver.LAUNCH");
registerReceiver(new MyReceiver(), filter);
前面兩步都完成了以后,就可以通過(guò)send方法來(lái)發(fā)送廣播了,如下所示。
Intent intent= new Intent()
intent.setAction("com.lsh.receiver.LAUNCH");
sendBroadcast(intent);
三 廣播的注冊(cè)過(guò)程
廣播的注冊(cè)分為靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè),其中靜態(tài)注冊(cè)的廣播在應(yīng)用安裝時(shí)由系統(tǒng)自動(dòng)完成注冊(cè),具體來(lái)說(shuō)是由PMS( PackageManager Service來(lái)完成整個(gè)注冊(cè)過(guò)程的,除了廣播以外,其他三大組件也都是在應(yīng)用安裝時(shí)由PMS解析并注冊(cè)的。這里只分析廣播的動(dòng)態(tài)注冊(cè)的過(guò)程,動(dòng)態(tài)注冊(cè)的過(guò)程是從 ContextWrapper 的 registerReceiver方法開(kāi)始的,和 Activity以及 Service一樣。 ContextWrapper并沒(méi)有做實(shí)際的工作,而是將注冊(cè)過(guò)程直接交給了Contextlmpl(這個(gè)文件是保護(hù)文件,就是注解了是內(nèi)部保護(hù)文件,所以無(wú)法找到,可以去SDk的安裝目錄中的sources文件夾中直接這個(gè)Java文件)完成,如下所
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
ContextImpl的 registerReceiver方法調(diào)用了自己的 registerReceiverInternal方法,它的實(shí)現(xiàn)如下所示:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
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();
}
}
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();
}
}
在上面的代碼中,系統(tǒng)首先從 mPackagelnfo獲取 IIntentReceiver對(duì)象,然后再采用跨進(jìn)程的方式向AMS發(fā)送廣播注冊(cè)的請(qǐng)求。之所以采用 IIntentReceiver而不是直接采用BroadcastReceiver,這是因?yàn)樯鲜鲎?cè)過(guò)程是一個(gè)進(jìn)程間通信的過(guò)程,而 BroadcastReceiver作為 Android的一個(gè)組件是不能直接跨進(jìn)程傳遞的,所以需要通過(guò) IIntentReceiver來(lái)中轉(zhuǎn)下。毫無(wú)疑問(wèn), IIntentReceiver必須是一個(gè) Binder接口,它的具體實(shí)現(xiàn)是 LoadedApk Receiver Dispatcher. InnerReceiver, Receiver Dispatcher的內(nèi)部同時(shí)保存了 BroadcastReceiver 和 Inner receiver,這樣當(dāng)接收到廣播時(shí), ReceiverDispatcher可以很方便地調(diào)用BroadcastReceiver的 on Receive方法??梢园l(fā)現(xiàn), BroadcastReceiver的這個(gè)過(guò)程和 Service的實(shí)現(xiàn)原理類似;
下面看一下 ReceiverDispatcher的 getlIntentReceiver的實(shí)現(xiàn), 如下所示。很顯然, getReceiverDispatcher方法重新創(chuàng)建了一個(gè) Receiver Dispatcher對(duì)象并,將其保存的 InnerReceiver對(duì)象作為返回值返回,其中 Inner Receiver對(duì)象和 BroadcastReceiver都是在 ReceiverDispatcher的構(gòu)造方法中被保存起來(lái)的。
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();
}
}
注冊(cè)廣播的真正實(shí)現(xiàn)是在 AMS 中,AMS的 registerReceiver 中,最終會(huì)把遠(yuǎn)程的 InnerReceiver 對(duì)象以及 IntentFilter 對(duì)象存儲(chǔ)起來(lái),這種整個(gè)廣播的注冊(cè)過(guò)程就完成了,如下代碼:
public Intent reiisterReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission,
int userId) {
...
mRegisterReceivers.put(receiver.asBinder(), rl);
BroadcastFilter broadcastFilter = new BroadcastFilter(filter, rl, callerPackage, permission,
callingUid, userId);
rl.add(bf);
mRegisterResolver.addFilter(bf);
...
}
四 廣播的發(fā)送和接受過(guò)程
廣播的發(fā)送和接收,其本質(zhì)是一個(gè)過(guò)程的兩個(gè)階段。這里從廣播的發(fā)送可以說(shuō)起,廣播的發(fā)送仍然開(kāi)始于 Context Wrapper的 sendBroadcast方法,之所以不是 Context,那是因?yàn)?Context的 send Broadcast是一個(gè)抽象方法。和廣播的注冊(cè)過(guò)程一樣, Context Wrapper的send Broadcast方法仍然什么都不做,只是把事情交給 ContextImpl去處理:
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
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();
}
}
從上邊看 ContextImpl 直接向 AMS 發(fā)起了一個(gè)異步請(qǐng)求用于發(fā)送廣播。
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;
}
}
broadcastIntent 調(diào)用了 broadcastIntentLocked 方法,AMS 的broadcastIntentLocked,在此方法中加了了
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
表示在 Android5.1 中 默認(rèn)情況下廣播不會(huì)發(fā)送給已停止的應(yīng)用了。
在BroadcastIntentLockes的內(nèi)部,會(huì)根據(jù)意圖intent-Fl1ter查找出匹配的廣播接收者并 經(jīng)過(guò)一系列的條件過(guò)濾,最終會(huì)將滿足條件的廣播接收者添加到Broadcast Queue中,接著B(niǎo)roadcast Queue就會(huì)將廣播發(fā)送給相應(yīng)的廣播接收者:
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);
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());
final BroadcastRecord oldRecord =
replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
// Replaced, fire the result-to receiver.
if (oldRecord.resultTo != null) {
final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
try {
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
false, false, oldRecord.userId);
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ queue.mQueueName + "] sending broadcast result of "
+ intent, e);
}
}
} else {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
下邊看一下 BroadcastQueue 中廣播的發(fā)送過(guò)程實(shí)現(xiàn),如下
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的scheduleBroadcastsLocked 方法并沒(méi)有立即發(fā)送廣播,而是發(fā)送了一個(gè) BROADCAST_INTENT_MSG ,BroadcastQueue收到消息后會(huì)調(diào)用 process-NextBroadcast 方法,BroadcastQueue 的 processNextBroadcast 方法對(duì)普通廣播處理如下:
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
System.identityHashCode(r));
}
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);
}
可以看到,無(wú)序廣播存儲(chǔ)在mParallelBroadcasts中,系統(tǒng)會(huì)遍歷mParallelBroadcasts并將其中的廣播發(fā)送給它們所有的接收者,具體的發(fā)送過(guò)程是通過(guò)delivetToRegisteredReceiveLoack 方法來(lái)實(shí)現(xiàn)的。delivetToRegisteredReceiveLoack方法負(fù)責(zé)將一個(gè)廣播發(fā)送給一個(gè)特定的接收者,它內(nèi)部調(diào)用了performReceiveLocked來(lái)完成具體的發(fā)送過(guò)程;
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
該方法實(shí)現(xiàn)如下
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);
// 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);
}
}
ApplicationThread的 scheduleRegisterReceiver 的實(shí)現(xiàn)比較簡(jiǎn)單,他是通過(guò) InnerReceiver 來(lái)實(shí)現(xiàn)廣播的接收。
void scheduleRegisterReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras,
boolean ordered, boolean sticky, int sendingUser,
int processState) {
updateProcessState(processState, false);
receiver.performReceiver(intent, resultCode, dataStr, extras, ordered, sticky, sendingUser);
}
InnerReceiver的 performReceiver方法會(huì)調(diào)用LoadApk.ReceiverDispatch的 perform-Receiver 方法
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 (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
+ " seq=" + seq + " to " + mReceiver);
}
}
if (intent == null || !mActivityThread.post(args.getRunnable())) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManager.getService();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
}
}
創(chuàng)建了一個(gè) Args 對(duì)象并通過(guò) mAcvitityThread 的 post 方法來(lái)執(zhí)行 Args 中的邏輯,
而args實(shí)現(xiàn)了Runnable接口.mActivityThread是一個(gè)Handler,它其實(shí)就是ActivityThread中的mh,mh的類型是ActivityThread的內(nèi)部類Н,關(guān)于H這個(gè)類前面已經(jīng)介紹過(guò)了,這里就不再多說(shuō)了.在args的方法中有如下幾行代碼:
很顯然,這個(gè)時(shí)候BroadcastReceiver的onReceiver方法被執(zhí)行了,也就是說(shuō)應(yīng)用已經(jīng)接收到廣播了,同時(shí) onReceiver 方法是在廣播接收者的主線程中被調(diào)用的.到這里,整個(gè)廣播的注冊(cè)、發(fā)送和接收過(guò)程已經(jīng)講解完畢
參考資料:
《Android開(kāi)發(fā)藝術(shù)探索》