广播的注册过程
首先,想要使用广播就得通过 registerReceiver 方法注册一个广播,最终也是在 Activity 的父类 ContextWrapper 中实现,代码如下:
//ContextWrapper.java
/**
* 应用调用注册广播的函数
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
*
* @return
*/
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
//mBase 是 Context的引用
return mBase.registerReceiver(receiver, filter);
}
直接看 Context 的实现类 ContextImpl 中的实现,代码如下:
/**
* Context 调用
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
*
* @return
*/
@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(), 0);
}
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
1
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
//获取 ActivityThread H
scheduler = mMainThread.getHandler();
}
//获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
2
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
3
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
//调用 AMS 的 registerReceiver
4
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();
}
}
总结如上步骤
1 判断 mPackageInfo 与 context 是否为空
2 通过 mPackageInfo 方法调用 getReceiverDispatch 方法获取 rd 对象
3 处的代码来创建 rd 对象
2 步代码主要就是来获取 IIntentReceiver 对象,它是一个 Binder 接口,用于广播的跨进程通信,它在在 LoadedApk.ReceiverDispatcher.InnerReceiver 中实现,代码如下所示:
//LoadedApk.java
static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
....
}
回到 registerReceiverInternal 方法,在注释 4 处调用 AMS 代理类 IActivityManager 的 registerReceiver 方法,通过进程间通信最后在 AMS 类的 registerReceiver 方法,并将 IIntentReceiver 类型的 rd 引用也一并传递进 AMS 中,这里之所以不直接传入客户端的 BroadcastReceiver 而是传入 IIntetnReceiver ,因为注册广播是一个跨进程的过程,需要具有跨进程通信功能的 IIntentReceiver ,你也可以把它理解为一个中间者,registerReceiver 方法内部比较多,这里分为两部分
我们先看
registerReceiver 方法的第一部分
//AMS.java
/**
* 这里是通过 Binder 通知调用
* @return Intent
*/
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
final boolean visibleToInstantApps
= (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
int callingUid;
int callingPid;
boolean instantApp;
synchronized(this) {
if (caller != null) {
1
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
instantApp = isInstantApp(callerApp, callerPackage, callingUid);
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
2
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
// Collect stickies of users
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
//根据actions 列表和 userIds 得到所有的粘性广播的 intent,并在注释 3 处传入到 stickyIntents 中
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>();
}
3
stickyIntents.addAll(intents);
}
}
}
}
}
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
// Look for any matching sticky broadcasts...
/**
* 遍历寻找匹配的粘性广播
*/
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
// Don't provided intents that aren't available to instant apps.
if (instantApp &&
(intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
continue;
}
//开始匹配
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
4
allSticky.add(intent);
}
}
}
....//后面代码 第二部分 讲解
return sticky;
}
}
总结如上
1 通过 getRecordForAppLocked 方法得到 ProcessRecord 类型的 callerApp 对象,它用于描述请求 AMS 注册广播接收者的 Activity 所在的应用程序进程
2 处根据传入的 IntentFilter 类型的 filter 得到一个 actions 列表,根据 actions 列表和 userIds 得到所有的粘性广播的 intent
3 处传入到 stickyIntents 中。接下来从 stickyIntent 中匹配传入的参数 filter 的粘性广播的 intent
4 处将这些 intent 存入到 allSticky 列表中,从这里可以看出粘性广播是存储在 AMS 中的。
registerReceiver 方法的 第二部分
接下来看 AMS 的 registerReceiver 方法剩余内容,代码如下所示:
//AMS.java
/**
* 这里是通过 Binder 通知调用
* @return Intent
*/
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags) {
...
synchronized (this) {
...
1
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {//如果为空调用注释 2
2
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);
} else if (rl.uid != callingUid) {
...
}
3
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
4
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
5
mReceiverResolver.addFilter(bf);
...
}
总结 如上代码
1. 获取 ReceiverList
2. 创建一个 ReceiverList 对象,它继承自 ArrayList,用于存放广播接收者
3 构建 BroadcastFilter 对象,并且将 广播接收者列表 ReceiverList 添加到进去 * 用于描述注册的广播接收者
4. 通过 add 函数将自身添加到广播接收者列表中
5. 将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中
在注释 1 处获取到了 ReceiverList 列表,如果为空则在注释 2 处创建一个新的接收列表 ReceiverList 对象,它继承自 ArrayList,用来存储广播接收者。在注释 3 处创建 BroadcastFilter 并传入此前创建的 ReceiverList ,BroadcastFilter 用来描述注册的广播接收者,并在注释 4 处通过 add 方法将自身添加到 ReceiverList 中。在注释 5 处将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中,这样当 AMS 接收到广播时就可以从 mReceiverResolver 中找到对应的广播接收者了。从而达到注册广播的目的。