一 BroadcastReceiver 简介
BroadcastReceiver 是一种消息型组件,称之为广播,用于在不同的组件乃至不同的应用之间传递消息。
广播的注册分为两种方式:
- 静态注册
静态注册是指在 AndroidManiifest中注册广播,这种广播在应用安装时会被系统解析、不需要应用启动就可以收到相应的广播. - 动态注册。
动态注册广播需要通过Context.registrerReceiver() 来实现,并且在不需要的时候要通过Context.unregistrerReceiver() 来解除广播,此种形态的广播必须要应用启动才能注册并接收广播。
广播收发器组件可以用来实现低耦合的观察者模式,观察者和被观察者之间可以没有任何耦合,由于BroadcastReceiver的特性,它不适合用来执行耗时操作.BroadcastReceiver一般来说不需要停止,它也没有停止的概念。
二 BroadcastReceiver 的使用
我们回顾一下广播的使用方法,首先要册
定义广播接收者,只需要继承 BroadcastReceiver并重写 on Receive方法即可,下面是一个典型的广播接收者的实现:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// onReceive函数不能做耗时的事情,参考值:10s以内
String action= intent. getAction ();
// do something
}
}
定义好了广播接收者,接着还需要注册广播接收者,注册分为两种方式,既可以在AndroidManifest
文件中静态注册,也可以通过代码动态注册。
静态注册的示例如下
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.lsh.receiver.LAUNCH"/>
</intent-filter>
</receiver>
通过代码来动态注册广播也是很简单的,如下所示。需要注意的是,动态注册
需要在合适的时机进行解注册,解注册采用 unregisterReceiver方法。
IntentFilter filter = new IntentFilter();
filter.addAction("com.lsh.receiver.LAUNCH");
registerReceiver(new MyReceiver(), filter);
前面两步都完成了以后,就可以通过send方法来发送广播了,如下所示。
Intent intent= new Intent()
intent.setAction("com.lsh.receiver.LAUNCH");
sendBroadcast(intent);
三 广播的注册过程
广播的注册分为静态注册和动态注册,其中静态注册的广播在应用安装时由系统自动完成注册,具体来说是由PMS( PackageManager Service来完成整个注册过程的,除了广播以外,其他三大组件也都是在应用安装时由PMS解析并注册的。这里只分析广播的动态注册的过程,动态注册的过程是从 ContextWrapper 的 registerReceiver方法开始的,和 Activity以及 Service一样。 ContextWrapper并没有做实际的工作,而是将注册过程直接交给了Contextlmpl(这个文件是保护文件,就是注解了是内部保护文件,所以无法找到,可以去SDk的安装目录中的sources文件夹中直接这个Java文件)完成,如下所
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
ContextImpl的 registerReceiver方法调用了自己的 registerReceiverInternal方法,它的实现如下所示:
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();
}
}
在上面的代码中,系统首先从 mPackagelnfo
获取 IIntentReceiver
对象,然后再采用跨进程的方式向AMS发送广播注册的请求。之所以采用 IIntentReceiver
而不是直接采用BroadcastReceiver
,这是因为上述注册过程是一个进程间通信的过程,而 BroadcastReceiver
作为 Android
的一个组件是不能直接跨进程传递的,所以需要通过 IIntentReceiver
来中转下。毫无疑问, IIntentReceiver
必须是一个 Binder接口,它的具体实现是 LoadedApk Receiver Dispatcher. InnerReceiver, Receiver Dispatcher
的内部同时保存了 BroadcastReceiver
和 Inner receiver
,这样当接收到广播时, ReceiverDispatcher
可以很方便地调用BroadcastReceiver
的 on Receive
方法。可以发现, BroadcastReceiver
的这个过程和 Service的实现原理类似;
下面看一下 ReceiverDispatcher
的 getlIntentReceiver
的实现, 如下所示。很显然, getReceiverDispatcher
方法重新创建了一个 Receiver Dispatcher
对象并,将其保存的 InnerReceiver
对象作为返回值返回,其中 Inner Receiver
对象和 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();
}
}
注册广播的真正实现是在 AMS 中,AMS的 registerReceiver 中,最终会把远程的 InnerReceiver 对象以及 IntentFilter 对象存储起来,这种整个广播的注册过程就完成了,如下代码:
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);
...
}
四 广播的发送和接受过程
广播的发送和接收,其本质是一个过程的两个阶段。这里从广播的发送可以说起,广播的发送仍然开始于 Context Wrapper
的 sendBroadcast
方法,之所以不是 Context
,那是因为 Context
的 send Broadcast
是一个抽象方法。和广播的注册过程一样, 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 发起了一个异步请求用于发送广播。
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 调用了 broadcastIntentLocked 方法,AMS 的broadcastIntentLocked,在此方法中加了了
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
表示在 Android5.1 中 默认情况下广播不会发送给已停止的应用了。
在BroadcastIntentLockes的内部,会根据意图intent-Fl1ter查找出匹配的广播接收者并 经过一系列的条件过滤,最终会将满足条件的广播接收者添加到Broadcast Queue中,接着Broadcast Queue就会将广播发送给相应的广播接收者:
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 中广播的发送过程实现,如下
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 方法并没有立即发送广播,而是发送了一个 BROADCAST_INTENT_MSG
,BroadcastQueue收到消息后会调用 process-NextBroadcast 方法,BroadcastQueue 的 processNextBroadcast 方法对普通广播处理如下:
// 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);
}
可以看到,无序广播存储在mParallelBroadcasts中,系统会遍历mParallelBroadcasts并将其中的广播发送给它们所有的接收者,具体的发送过程是通过delivetToRegisteredReceiveLoack 方法来实现的。delivetToRegisteredReceiveLoack方法负责将一个广播发送给一个特定的接收者,它内部调用了performReceiveLocked来完成具体的发送过程;
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
该方法实现如下
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
的实现比较简单,他是通过 InnerReceiver 来实现广播的接收。
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方法会调用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);
}
}
}
}
创建了一个 Args 对象并通过 mAcvitityThread 的 post 方法来执行 Args 中的逻辑,
而args实现了Runnable接口.mActivityThread是一个Handler,它其实就是ActivityThread中的mh,mh的类型是ActivityThread的内部类Н,关于H这个类前面已经介绍过了,这里就不再多说了.在args的方法中有如下几行代码:
很显然,这个时候BroadcastReceiver的onReceiver方法被执行了,也就是说应用已经接收到广播了,同时 onReceiver 方法是在广播接收者的主线程中被调用的.到这里,整个广播的注册、发送和接收过程已经讲解完毕
参考资料:
《Android开发艺术探索》