前言
一直想梳理一下广播的注册和发送流程。有次面试我被问了许多关于广播的问题,很多问题都能从源码中找到答案,其中一些我也看过源码,但当时问的时候完全懵逼了。有些问题甚至没有深入考虑过,问的时候全靠猜。所以在此是做个梳理,也希望读者读完这篇文章,能帮助你解决以下问题。
1、onReceive执行在哪个线程
2、广播是如何发送的
3、进程没启动的时候接收静态广播是否会启动进程
PS:写关于源码的文章自己也比较头疼,不知道从何开始从何结束,源码复杂不说很多内容还交叉在一起。过于深入自己又感觉得不偿失,毕竟不是做framework的。所以对文章有任何意见或者建议,欢迎留言。
广播是如何发送和接收的
广播的发送主要分为两步。第一步是收集匹配的广播接收器,第二步就是给匹配的接收器发送广播。
我们从activity#sendBroadcast
来说说整个广播的发送流程。首先需要明白,调用sendBroadcast
的时候,请求都被转发到了ContextImpl
中。ContextWrapper
其实是ContextImpl
的包装类,这是一个装饰者设计模式。这里尽量不大范围贴源码了,我画了个类图。
我们直接从ContextImpl
开始看起,这里其实也只是把请求交给了ActivityManagerProxy
,broadcastIntent
方法就是通过binder发起一个异步请求给AMS,而在AMS的broadcastIntent
方法中又调用了broadcastIntentLocked
方法。
ContextImpl.class
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().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();
}
}
ActivityManagerProxy.class
public int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
boolean sticky, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null);
data.writeInt(resultCode);
data.writeString(resultData);
data.writeBundle(map);
data.writeStringArray(requiredPermissions);
data.writeInt(appOp);
data.writeBundle(options);
data.writeInt(serialized ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
data.writeInt(userId);
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);//通过binder调用broadcastIntent方法
reply.readException();
int res = reply.readInt();
reply.recycle();
data.recycle();
return res;
}
可以看到这里通过binder调用了AMS的broadcastIntent
方法。里面调用了broadcastIntentLocked
,其中代码非常多。包括权限检查和一些系统广播的处理,这里就关注静态广播、动态广播的查询和发送。
广播接收器的收集
Step1 静态广播接收器的查询
在该方法中调用了collectReceiverComponents
查询和intent匹配的静态广播。
ActivityManagerService.class
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users) {
//...
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
//...
return receivers;
}
可以看到,这里通过PMS的queryIntentReceivers
方法查询和intent匹配的广播。这也就说明,静态广播是由PMS管理的。
Step2 动态广播接收器的查询
动态广播通过queryIntent
查询
ActivityManagerService.class
if (intent.getComponent() == null) {
if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
// Query one target user at a time, excluding shell-restricted users
for (int i = 0; i < users.length; i++) {
if (mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
continue;
}
List<BroadcastFilter> registeredReceiversForUser =
mReceiverResolver.queryIntent(intent,
resolvedType, false, users[I]);
if (registeredReceivers == null) {
registeredReceivers = registeredReceiversForUser;
} else if (registeredReceiversForUser != null) {
registeredReceivers.addAll(registeredReceiversForUser);
}
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
}
广播的发送
Step1 发送广播给动态广播接收器
根据查询出的receivers构造一个BroadcastRecord
,接着调用enqueueParallelBroadcastLocked
将广播接收者添加到BroadcastQueue
中。最后调用scheduleBroadcastsLocked
,这个方法的逻辑主要是把广播发送到之前所收集的广播接收者当中。
ActivityManagerService.class
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
在scheduleBroadcastsLocked
中发送了一个BROADCAST_INTENT_MSG
消息。
BroadcastQueue.class
public void scheduleBroadcastsLocked() {
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
BroadcastQueue
收到消息会调用processNextBroadcast
方法。在该方法中会先发送无序广播。看一下发送无序广播的逻辑。其他类型的广播发送大同小异。
BroadcastQueue.class
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
}
在其内部调用deliverToRegisteredReceiverLocked
,该方法内部又通过performReceiveLocked
把消息发送给特定的广播接收器。
BroadcastQueue.class
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
if (app != null) {
if (app.thread != null) {
//调用scheduleRegisteredReceiver发送广播到接收器
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky,sendingUser,app.repProcState);
//...
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
这里的app.thread
是ApplicationThread
,一般都不为null。ApplicationThread
是ActivityThread
的内部类。
ActivityThread.class
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
IIntentReceiver
对象的performReceive
会调用LoadedApk.ReceiverDispatcher
的performReceive
方法。在其内部会构建一个Args对象,该类实现的了Runnable接口,接着通过mActivityThread
执行args
。
LoadedApk.class#Args.class
public void run() {
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
final IActivityManager mgr = ActivityManagerNative.getDefault();
final Intent intent = mCurIntent;
mCurIntent = null;
mDispatched = true;
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
}
}
可以看到args对象的run方法最终调用了receiver.onReceive
。
所以onReceive方法在哪个线程执行?
在主线程。因为广播接收最后会通过mH回调到主线程去执行onReceive方法,所以onReceive是执行在主线程的。
Step2 发送广播给静态广播接收器
把广播发送到静态广播接收器的逻辑与发送到动态广播接收器的逻辑大同小异,都需要调用到processNextBroadcast
中。需要注意区分两种情况,一种是进程已经启动,一种是进程尚未启动。
// Is this receiver's application already running?
if (app != null && app.thread != null) {
//...
processCurBroadcastLocked(r, app);
}
Situation1 进程已经启动
如果进程已经启动,就会调用processCurBroadcastLocked
发送广播。其中又会调用processCurBroadcastLocked
。其中又会调用app.thread.scheduleReceiver
发送广播。app.thread是ActivityThread。
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.repProcState);
ActivityThread.class
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
updateProcessState(processState, false);
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
sendMessage(H.RECEIVER, r);
}
这个方法很简单,就是发了一个RECEIVER
消息。所以静态广播的onReceive方法也会运行在主线程。mH
接收到这个消息会调用到handleReceiver
,通过反射构建receiver实例。
Situation2 进程未启动
如果进程没有启动呢?接着看回processNextBroadcast
,在该方法的最后面有这样一段逻辑,它主要的作用就是拉起app进程。如果应用已经启动,是不会执行到这段逻辑。startProcessLocked
这个方法需要特别留意,这个方法就是用来拉起进程的。
if ((r.curApp=mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
所以进程没启动的时候接收静态广播会启动进程,并且会调用Application的onCreate方法。
总结
整个广播发送的流程大致如下
1.查询和Intent匹配的静态广播接收器
2.查询和intent匹配的动态广播接收器
3.发送广播给动态广播接收器
4.发送广播给静态广播接收器
最后通过开篇的三个问题总结一下这篇的内容
- onReceive执行在哪个线程
- 主线程
- 广播是如何发送的
- 利用Binder机制通过AMS发送,最终都通过mH回调到主线程
- 进程没启动的时候接收静态广播是否会启动进程
- 会启动进程
网友评论