Android进阶知识(二十九):BroadcastReceiver的工作过程
BroadcastReceiver是一种消息型组件,用于在不同的组件乃至不同的应用之间传递消息。BroadcastReceiver的工作过程主要包含两个方面,一个是广播的注册过程,另一个是广播的发送和接收过程。
至于BroadcastReceiver的使用这里就不介绍了,另外在Android 8.0之后,Google对静态注册的广播有做了一定的使用限制,关于这方面的知识读者可以见笔者的两篇笔记,这里不多介绍:
一、BroadcastReceiver的注册过程
广播的注册分为静态注册(在AndroidManifest文件中注册)和动态注册(代码注册)。静态注册的广播在应用安装时由系统自动完成注册,具体来说是由PMS(PackageManagerService)完成整个注册过程。除了广播,其他三大组件也都是在应用安装时由PMS解析并注册的。
这里我们只分析广播的动态注册过程,其具体流程如下图所示。
其中,黑色虚线箭头表示调用了重载方法,最终调用箭头指向方法;紫色虚线箭头表示远程调用的IPC过程。对于BrocadcastReceiver的注册过程,需要注意几点。
- 注册过程是从ContextWrapper开始
同Service的启动过程类似,广播的注册方法registerReceiver实际实现是在ContextWrapper中实现的,因此其启动过程也是从ContextWrapper开始的。
- ContextImpl的registerReceiverInternal方法
ContextImpl的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();
}
}
该方法同Service绑定过程的bindServiceCommon工作类似:首先将BroadcastReceiver对象封装成IntentReceiver对象;接着调用AMS的registerReceiver实现注册过程。
同样的,之所以进行封装原因为:第一,BroadcastReceiver作为Android的一个组件是不能直接跨进程传递的,需要通过IIntentReceiver这个Binder进行中转,具体实现为ReceiverDispatcher.InnerReceiver;第二,ReceiverDispatcher内部同时保存了BroadcastReceiver和InnerReceiver,以方便后续BroadcastReceiver的onReceive方法的调用。
- 注册过程registerReceiver方法只保存,不回调
广播的注册过程最为不同的一点是,在远程调用AMS的registerReceiver后,该方法只进行InnerReceiver对象以及IntentFilter对象的保存,而无需像Activity和Service流程那样进行远程方法回调。其核心代码如下。
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
// ...
// 保存IntentReceiver对象
mRegisteredReceivers.put(receiver.asBinder(), rl);
// ...
// 保存IntentFilter对象
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
if (rl.containsFilter(filter)) {
Slog.w(TAG, "Receiver with filter " + filter
+ " already registered for pid " + rl.pid
+ ", callerPackage is " + callerPackage);
} else {
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);
}
// ...
}
至此广播的注册过程就分析完了。
二、BroadcastReceiver的发送和接收过程
对于广播的发送和接收过程,当通过send方法来发送广播时,AMS会查找出匹配的广播接收者并将广播发送给它们处理。广播的发送有几种类型:普通广播、有序广播和粘性广播,即使特性不同当流程都是类似的,因此这里笔者以普通广播为例子进行分析。
广播的发送和接收过程需要注意的几点如下。
- 广播不会发送给已经停止的应用
broadcastIntentLocked方法比较复杂,但是代码里面有这么一行。
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
从Android 3.1开始(此处用到是Android 10.0)广播有一种特性,即默认情况下广播不会发送给已经停止的应用。在Android 3.1中为Intent添加了两个标记位,分别为FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,用于控制广播是否要对处于停止状态的应用起作用,具体含义如下。
标记位 | 含义 |
---|---|
FLAG_INCLUDE_STOPPED_PACKAGES | 表示包含已经停止的应用,这个时候广播会发送给已经停止的应用 |
FLAG_EXCLUDE_STOPPED_PACKAGES | 表示不包含已经停止的应用,这个时候广播不会发送给已经停止的应用 |
之所以默认不会发送广播给已经停止的应用,是为了防止广播无意间或者在不必要的时候调起已经停止的应用。如果需要调起已经停止的应用,只需要为广播的Intent添加FLAG_INCLUDE_STOPPED_PACKAGES标记位即可,这两个标记共存时以FLAG_EXCLUDE_STOPPED_PACKAGES为准。
- broadcastIntentLocked工作
在该方法内部会根据intent-filter查找出匹配的广播接收者并经过一系列条件过滤,最终将满足条件的广播接收者添加到BroadcastQueue中,接着调用BroadcastQueue的scheduleBroadcastsLocked方法发送广播。部分源码如下所示。
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);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, timeoutExempt);
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) {
// 保存普通广播,并发送
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
至此,广播的注册、接收以及发送过程就分析完了。
参考资料:《Android开发艺术探索》