主目录见:Android高级进阶知识(这是总目录索引)
同时预告下,以后可能会来翻译groovy语言的文档,地址:Groovy Language Documentation,如果想要参与翻译的可以跟我说哈。
我们知道注册广播有两种方式,一种是动态广播注册,另一种是静态广播注册,正因为这个使得插件化开发中广播的注册也相对简单,我们可以利用动态广播注册的方式把我们插件中的广播进行注册。今天这里也同样为了完整性,也来讲一讲广播在FrameWork中的流程,包括注册和发送两个过程。跟Activity和Service类似,广播也是要与AMS打交道。
一.动态广播注册
在前两篇讲Activity
和Service
中我们应该已经知道了Activity
,Service
都是继承的ContextWrapper
类,而且了解装饰模式的应该知道,ContextWrapper
在这里只是一个装饰组件角色,具体的组件角色是ContextImpl
,所以我们在Activity
或者Service
中调用registerReceiver()
来注册广播的时候,实际最后调用的是ContextImpl
中的registerReceiver()
方法。所以我们直接从这里开始看:
@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());
}
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
//这个Handler是后面用来分发AMS发送的广播用的,可以看到这里是在主线程中分发,
//如果你想要在子线程中分发,可以自己指定一个Handler
scheduler = mMainThread.getHandler();
}
//mPackageInfo是LoadedApk实例,这个方法主要是获取 rd,这是一个IIntentReceiver对象,
//可以看出这是一个Binder本地对象,具有跨进程的能力
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
......
}
}
try {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
我们看到程序里面如果scheduler
为空就会从mMainThread
中获取,这里的mMainThread
是一个ActivityThread实例,在ActivityManagerService
发送广播过来用这个来分发的。而且我们看到mPackageInfo
是个LoadedApk
实例,getReceiverDispatcher()
方法获取到的是一个Binder本地对象,具体我们看下这个方法:
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) {//这个是true
map = mReceivers.get(context);
if (map != null) {
//查看是否有相应的dispatcher了
rd = map.get(r);
}
}
if (rd == null) {
//没有则新建一个,这里的context可能是Activity,Service等
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 {
//检查广播的context,handler是否一致
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
从上面的方法可以看出,首先是从mReceivers
中查找有没有对应context对应的map,这里的mReceivers
是LoadedApk
的一个成员变量,这里它是一个以Activity
即context为key,value为map的数据结构.然后根据我们要发送的BroadcastReceiver
我们就可以查找到广播接收发布器ReceiverDispatcher
。但是这里的ReceiverDispatcher
是个什么东西呢?我们一起来看看:
static final class ReceiverDispatcher {
//Binder本地对象,用于跨进程通讯
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;
}
//知道AIDL写法的人应该知道,这是远程会调用到的方法
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
......
}
}
......
ReceiverDispatcher(BroadcastReceiver receiver, Context context,
Handler activityThread, Instrumentation instrumentation,
boolean registered) {
//Binder本地对象,用于跨进程通讯
mIIntentReceiver = new InnerReceiver(this, !registered);
//广播接收者
mReceiver = receiver;
//广播的发送者
mContext = context;
//这是主线程,后面消息分发也是在这个类中的H类中
mActivityThread = activityThread;
.......
}
.....
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
.....
}
我们看到ReceiverDispatcher
在构造函数中会创建一个InnerReceiver
实例,这是一个Binder
对象,实现了IIntentReceiver
接口,我们可以通过ReceiverDispatcher#getIIntentReceiver
来获取,然后会把这个对象注册到AMS中,以便用来接收广播。同时,构造函数里面会保存activityThread
以便广播分发的时候使用,所以到这里我们知道getReceiverDispatcher()
方法返回的就是这个InnerReceiver
实例。
然后我们回到registerReceiverInternal
方法,这个方法获取了InnerReceiver
类型的Binder
对象之后,就直接注册到AMS了。然后我们看AMS中的registerReceiver
方法:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
synchronized(this) {
if (caller != null) {
//获取当前进程对象
callerApp = getRecordForAppLocked(caller);
......
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
//获取IntentFilter中的action
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
//从actions中,先把粘性广播帅选出来,放进stickyIntents中
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
//从mStickyBroadcasts中查看用户的sticky Intent,mStickyBroadcasts存了系统所有的
//粘性广播,为什么叫做Sticky Intent,就是这个最后发出的广播虽然被处理完了,但是仍然被粘住在ActivityManagerService中,以便下一个注册相应Action类型的广播接收器还能继承处理
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);
}
}
}
}
}
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);
// If intent has scheme "content", it will need to acccess
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
// The first sticky in the list is returned directly back to the client.
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
if (receiver == null) {
return sticky;
}
synchronized (this) {
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// Original caller already died
//进程不存在(死亡了),也是不能注册成功的
return null;
}
//这里其实就是把广播接收器receiver保存一个ReceiverList列表中,这个列表的宿主进程是rl.app,
//这里就是MainActivity所在的进程了,在AMS中,用一个进程记录块来表示这个应用程序进程,
//它里面有一个列表receivers,专门用来保存这个进程注册的广播接收器。
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
// //把广播接收者列表加到这个进程对象的receivers中
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) {
.......
} else if (rl.pid != callingPid) {
........
} else if (rl.userId != userId) {
........
}
//由filter,rl等参数构建一个BroadcastFilter,看名字可以知道每个广播接收者就是
//一个BroadcastFilter,这是动态广播注册时候封装的广播接收者对象
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
//如果是粘性广播,创建BroadcastRecord,并添加到
//BroadcastQueue的并行广播队列(mParallelBroadcasts),
//注册后调用AMS来尽快处理该广播。
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
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;
}
}
我们看到AMS中维护了mRegisteredReceivers
用来记录广播接收者,首先会根据客户端传进来的Binder对象InnerReceiver
获取ReceiverList
,所以我们知道,一个客户端ReceiverDispatcher
对应于一个ReceiverList
,最后会把这个广播接收者构建出BroadcastFilter
对象,然后保存到ReceiveList
中,到这里动态广播注册就已经注册完毕。
2.静态广播注册
其实我们插件化开发中用的都是动态广播来处理,但是既然将广播那这也是不得不说一下,我们知道manifest文件的解析是PackageParser
来解析的。所以静态广播也不例外,在程序安装的时候,PackageParser
会对apk中的xml进行扫描,然后把组件消息进行记录。我们在PackageParser
的parseSplitApplication
方法中可以看到:
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
.....
} else if (parser.getName().equals("meta-data")) {
.....
}else if ....
}
我们看到这段代码很简单,就是用pullparser
解析xml文件,然后把相应的节点解析放在内部类Package相应的内部变量中,这里的receiver是放在owner#receivers
中,然后AMS在collectReceiverComponents()
会通过调用PMS(PackageManagerService)
的方法queryIntentReceivers()
来查找注册的广播接收者:
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId));
}
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForResolve(flags, userId, intent);
//获取组件名称,这里就是receiver的名称
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
//获取广播的信息
ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
// 根据intent返回manifest中配置的receiver的信息,封装在ResolveInfo
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
userId);
}
return Collections.emptyList();
}
}
我们看到,我们动态广播是动态调用registerReceiver
在往AMS注册广播,静态广播是通过PP解析xml文件注册广播,然后AMS通过访问PMS的方法来获取注册的广播。整体流程还是很清晰的,如果不知道,请大家认真再看看。
总结:注册广播的流程不是很复杂,这里我建议大家多关注动态广播注册,因为到时会用到,大家大概有个印象,希望大家不要厌恶看源码,看多了自然就习惯了,希望大家旅途愉快。