文中的源代码版本为api23
广播注册分为两种:动态注册、静态注册。
静态注册只需要在Manifest文件中声明即可。
而动态注册则需要通过Context.registerReceiver方法进行注册。下面我们分析一下动态注册的过程。
动态广播注册
动态注册通过调用Context.registerReceiver方法实现。该方法会经历以下调用链
Context.registerReceiver(BroadcastReceiver,IntentFilter)->
ContextImpl.registerReceiver(BroadcastReceiver,IntentFilter)->
ContextImpl.registerReceiver(BroadcastReceiver,IntentFilter,String,Handler)->
ContextImpl.registerReceiverInternal
最终进入ContextImpl.registerReceiverInternal方法中
1. ContextImpl.registerReceiverInternal方法
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter,
String broadcastPermission,//广播发送者需要具备该权限才能发广播给这个接收者
Handler scheduler,//scheduler可以控制广播接收者运行所在的线程
Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
//将receiver包装成一个aidl接口
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
//将receiver包装成一个aidl接口
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}
该方法结构较简单,做了两件事情
- 将
receiver包装成一个aild接口(IIntentReceiver)。后续AMS可以直接与该接口进行通信,分发广播事件。 - 跨进程调用
ActivityManagerService.registerReceiver方法
2. ActivityManagerService.registerReceiver方法
该方法主要做了两件事情
- 在
AMS中保存广播接收者(实际上就是IIntentReceiver这个aidl接口) - 发送粘性广播 其中粘性广播的逻辑占据了非常大的篇幅
1. 保存广播接收者
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
//...
//mRegisteredReceivers保存着所有的广播接收者的binder对象
//key为IIntentReceiver的binder对象
//value为ReceiverList,该数据结构的作用是建立起广播接收者与广播之间的联系
//由于广播接收者和广播是一对多关系,因此ReceiverList派生了ArrayList
//第一次进来rl肯定是null
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
//创建并保存ReceiverList
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
//ProcessRecord也会记录ReceiverList
rl.app.receivers.add(rl);
} else {
//...
}
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);
} else if (rl.pid != callingPid) {
throw new IllegalArgumentException(
"Receiver requested to register for pid " + callingPid
+ " was previously registered for pid " + rl.pid);
} else if (rl.userId != userId) {
throw new IllegalArgumentException(
"Receiver requested to register for user " + userId
+ " was previously registered for user " + rl.userId);
}
// 创建一个BroadcastFilter
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
// 保存BroadcastFilter至ReceiverList
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
//添加至BroadcastFilter保存至BroadcastFilter,在发送广播的时候会用到
mReceiverResolver.addFilter(bf);
//...
}
总结一下流程
- 将广播接收者封装成
ReceiverList对象保存至mRegisteredReceivers - 封装
IntentFilter为BroadcastFilter保存至ReceiverList(ReceiverList本身派生自ArrayList) 可以看到在AMS中广播接收者和IntentFilter是一对多的关系,因此我们可以使用一个广播接收者调用多次registerReceiver,来实现一个广播接收者监听多个广播的功能。
2. 发送粘性广播
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
//...
//寻找粘性广播,保存至allSticky
synchronized (this) {
//...
Iterator<String> actions = filter.actionsIterator();
//...
// 所有的粘性广播都保存在一个名为mStickyBroadcasts的变量中
// 类型为SparseArray<ArrayMap<String, ArrayList<Intent>>>
// SparseArray的key是userId,value是一个Map类型,该Map类型的
// key为action,value则是action对应的所有的粘性广播
// 以action为维度寻找当前用户下所有符合条件的粘性广播
// 并保存至stickyIntents中
int[] userIds = {UserHandle.USER_ALL, UserHandle.getUserId(callingUid)};
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);
}
}
}
}
}
// 遍历stickyIntents,以更加严苛的条件去匹配
// IntentFilter.match方法内部会就mimetype、scheme、data等
// 变量进行匹配
// 将所有符合条件的粘性广播保存至allSticky
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
//...
synchronized (this) {
//...保存receiver
if (allSticky != null) {
//...
//遍历所有粘性广播,使用BroadcastQueue进行广播
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
//将粘性广播封装成BroadcastRecord
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
//插入到广播队列中
queue.enqueueParallelBroadcastLocked(r);
//开始发送粘性广播
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
发送粘性广播的流程总结下来只有两步
- 寻找符合
IntentFilter条件的粘性广播 - 将粘性广播封装成
BroadcastRecord插入到广播队列中,然后通过BroadcastQueue发送粘性广播
在分析粘性广播寻找过程中我一度以为mStickyBroadcasts的key是uid(每个app一个),写了一些测试代码才发现其实是user id,这两个值其实是有区别的。如果大家感兴趣可以自行百度UserHandle。
由于广播的发送都是使用BroadcastQueue.scheduleBroadcastsLocked方法,因此关于粘性广播的具体发送过程,我们在分析广播发送流程的时候再讲。
3. 总结
动态广播注册过程概括来讲其实只做了两件事情
- 保存广播(也即是
Binder对象) - 发送粘性广播