本文基于Android 12进行广播流程的分析,主要从四个方面:广播的注册、解注册、处理、结束四方面进行分析,会比较全面、按个人理解对广播进行解析。但个人能力有限,可能存在部分理解错误,但绝对是一篇理解Android 广播流程的好文。和自己前年写的广播分析,简直打脸自己。原来,想要进步,就是推翻自己,重新再来。
在AMS中持有集合用于存储所有的广播,应用程序可以从向其注册和解注册广播。当应用发送广播时,AMS检查相关权限和特殊的Intent
。然后再根据对应IntentFilter
匹配到一个或多个Receiver
,在应用进程回调其onReceive
函数。
广播的注册
广播的注册分动态注册和静态注册两种,静态注册是指将BroadcastReceiver
和IntentFilter
写在配置清单里,Android系统在解析包信息的时候,会将其添加到PackageManagerService
的mComponentResolver
中,后续处理广播会从中查找匹配的接收者BroadcastReceiver
。而动态注册,指的是在程序运行后,通过代码进行注册,下面分析的是动态注册的内容。
我们常在Activity或Service、甚至在Application中调用registerReceiver函数来动态注册广播,该函数其实来自他们的父类ContextWrapper中。ContextWrapper是Context的子类,我们会在介绍Context的文章介绍它们的关系。
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
这里Context类型的mBase,在Activity的创建过程会被赋值为ContextImpl实例。
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext(), 0);
}
经过registerReceiver重载函数,接着调用registerReceiverInternal函数。
ContextImpl.registerReceiverInternal
registerReceiverInternal函数,执行到注释1处,说明当前进程还存活着的,通过LoadedApk对象mPackageInfo的getReceiverDispatcher函数,根据context从mReceivers容器中获ArrayMap对象map,再以receiver为Key在map查询对应的ReceiverDispatcher对象。也就是说,一个Context对象,可能是Activity、Application、Service,会注册一个或多个广播接收者BroadcastReceiver,而一个广播接收者对应一个ReceiverDispatcher。后续广播的处理会通过ReceiverDispatcher找到对应的BroadcastReceiver,将Intent传递给它处理。
如果查询不到ReceiverDispatcher对象,说明之前没有添加过,则创建ReceiverDispatcher对象(内部会创建InnerReceiver对象),按照前面获取的逻辑,进行逆操作,最终将receiver添加到mReceivers映射中。如果已经存在,则更新Context和Handler对象。
有了ReceiverDispatcher对象后,通过其getIIntentReceiver函数获得InnerReceiver对象。InnerReceiver继承自IIntentReceiver.Stub,说明InnerReceiver会在进程之间传递。
注释2,相对于注释1,少传递了Instrumentation对象,我们知道Instrumentation使用来监视系统与应用程序之间的交互的,注释2处,由于应用程序未启动完毕,所以不需要。
注释3调用了AMS的registerReceiverWithFeature函数。这时候离开了应用所在的进程。总结的说,这里缓存我们注册的BroadcastReceiver对象,将相关信息封装成IIntentReceiver对象,传递给了AMS。
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) {
//主线程的H对象,用于接收广播用于保持receivcer广播顺序到达问题
scheduler = mMainThread.getHandler();
}
//注释1:已注册,则获取旧的对象,否则新创建
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
//注释2
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
//注释3,返回值是第一个匹配IntentFilter的黏性广播的Intent或者null
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
filter, broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
AMS.registerReceiverWithFeature
我们知道,一个广播接收者(BroadcastReceiver)可能会多个匹配条件(IntentFilter)。AMS的registerReceiverWithFeature函数的主要功能是处理这层关系和存储它们。我们将涉及的几个重要成员用下图的关系表达出来。
-
mRegisteredReceivers:AMS的成员变量,类型为HashMap<IBinder, ReceiverList>,Hash Key是IIntentReceiver的IBinder对象,也就是客户端BroadcastReceiver在客户端的代表。而Value就是ReceiverList对象。
-
ReceiverList:继承自ArrayList<BroadcastFilter>,用于存储BroadcastFilter对象,表示一个BroadReceiver所对应的多个IntenerFilter的关系。
-
BroadcastFilter: 内部封装了IntentFilter,可以表示为IntentFilter在系统层的代表。
-
mReceiverResolver:存储BroadcastFilter,表示当前所有的IntentFilter对象。后续发送广播时,如果广播没有component,需要通过它区解析匹配的接收者。
registerReceiverWithFeature函数首先根据receiver.asBinder()在mRegisteredReceivers映射中查找是否有历史的ReceiverList对象。如果没有历史记录,则创建ReceiverList对象,并添加到mRegisteredReceivers中和receiver所在进程的ProcessRecord的mReceivers中。一个应用程序允许创建最多的ReceiverList对象数量是1000个。
接着会将相关信息,主要是IntentFilter,封装成BroadcastFilter对象。第一次会将该BroadcastFilter对象添加到对应的ReceiverList对象。也会添加到mReceiverResolver中。
registerReceiverWithFeature函数的另一个功能就是对黏性广播的处理。我们知道广播分三种类型:无序广播、有序广播、黏性广播。黏性广播的处理时机在此处,其他两个类型的处理可以看后文广播的发送小节。
先从AMS的mStickyBroadcasts数组中找出发给当前用户的Intent数组stickyIntents,包括发给当前用户和所有用户的黏性Intent。然后再在stickyIntents数组中找出与IntentFilter匹配的所有Intent,存储到allSticky中。然后根据allSticky数组,创建出每个Intent对应的BroadcastRecord(一个BroadcastRecord代表着一个广播),并将它们的sticky属性赋值为true,表示是黏性广播。然后加入到并行广播队列中,调用队列的 scheduleBroadcastsLocked的函数执行广播的处理流程。总结的说,就是找出与当前IntentFilter匹配的黏性广播,交给其receiver处理。
到这里,广播的注册就完成了。
广播的解注册
有了对广播注册流程的了解,那么对广播的解注册流程理解,会容易很多。也就是在注册过程中,在LoadedApk
对象加入到mReceiver
中,现在就要将其移除,并重置相关属性。在AMS中,注册时是加入mRegisteredReceivers
和ReceiverList
,以及mReceiverResolver
,那么解注册就是从它们移除。
有了对广播注册流程的了解,那么对广播的解注册流程理解,会容易很多。也就是在注册过程中,在LoadedApk对象加入到mReceiver中,现在就要将其移除,并重置相关属性。在AMS中,注册时是加入mRegisteredReceivers和ReceiverList,以及mReceiverResolver,那么解注册就是从它们移除。
回到ContextWrapper的unregisterReceiver函数。
//ContextWrapper
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
}
//ContextImpl
public void unregisterReceiver(BroadcastReceiver receiver) {
if (mPackageInfo != null) {
//LoadedApk
IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
getOuterContext(), receiver);
try {
//AMS
ActivityManager.getService().unregisterReceiver(rd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
throw new RuntimeException("Not supported in system context");
}
}
LoadedApk类forgetReceiverDispatcher函数的处理逻辑是和注册时getReceiverDispatcher函数反着来。根据context对象在mReceivers映射中获取当前context的所有BroadcastReceiver对应的ReceiverDispatcher对象。这是得到的是一个ArrayMap对象map,再在该map中根据BroadcastReceiver对象获取对应的ReceiverDispatcher。假如存在的话,依次从map、mReceiver(没有其他receiver情况下)中移除。如果不存在map对象中,还要从已解注册列表mUnregisteredReceivers中查看是否存在,如果存在,报下异常。
接着调用AMS的unregisterReceiver函数。AMS的解注册稍微复杂一点。如果此时正有有序广播在处理,那么要调用广播队列的finishReceiverLocked函数,如果返回结果为true,则调用队列的processNextBroadcastLocked将广播传递给下一个receiver。然后将ReceiverList对象从mRegisteredReceivers中移除,BroadcastFilter对象从mReceiverResolver中移除。
public void unregisterReceiver(IIntentReceiver receiver) {
final long origId = Binder.clearCallingIdentity();
try {
boolean doTrim = false;
synchronized(this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl != null) {
//当前正在处理的有序广播,可以参考=》广播的结束小节
final BroadcastRecord r = rl.curBroadcast;
if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
final boolean doNext = r.queue.finishReceiverLocked(
r, r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
//参考=》广播队列对广播的处理小节
r.queue.processNextBroadcastLocked(/* frommsg */ false,
/* skipOomAdj */ true);
}
}
//ProcessRecord移除
if (rl.app != null) {
rl.app.mReceivers.removeReceiver(rl);
}
//从mRegisteredReceivers移除
//从mReceiverResolver移除
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
rl.linkedToDeath = false;
rl.receiver.asBinder().unlinkToDeath(rl, 0);
}
}
//清理进程
if (doTrim) {
trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
return;
}
} finally {
Binder.restoreCallingIden
广播的处理
ContextWrapper的sendBroadcast函数,调用了ContextImpl类sendBroadcast函数,进而调用了AMS的broadcastIntentWithFeature函数。
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
//AMS
ActivityManager.getService().broadcastIntentWithFeature(mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
1、AMS对广播的处理
调用AMS的broadcastIntentWithFeature函数。
verifyBroadcastLocked函数,对Intent的合法性进行检查,下面三种情况都会抛出异常。
-
携带文件描述符;
-
系统未启动完毕,当前Intent发送给所有的接收者。
-
开机升级广播。
如果Intent携带Intent.FLAG_RECEIVER_FROM_SHELL的Flags,即该广播时从Shell发送出来,且调用者不是系统进程或者Shell进程的UID,则需要将该Flags移除,定义为普通的广播。
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, String[] excludedPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
synchronized(this) {
//对广播Intent合法性检查
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLOSP(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
//下一个调用,会在调用重载函数
return broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, excludedPermissions, appOp, bOptions, serialized,
sticky, callingPid, callingUid, callingUid, callingPid, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
接着调用了broadcastIntentLocked函数。根据该函数内部处理逻辑顺序,大概出下面主要功能。
-
Instant应用禁止发送广播给Instant应用接收者,即快应用之间禁止通过广播交互。
-
系统未启动完成(非升级过程),禁止通过广播启动新的进程。
-
广播发给特定用户,用户或者父用户没有运行,则不发送广播。
-
广播发送时,设置options数据,需要根据options情况,检查白名单、受限目标、后台启动Activity等权限进行检查。
-
检查广播Action,判断是否受保护的广播的Action,即声明在framework/base/core/res/AndroidMenifest.xml文件内的protected-broadcast,这些广播只能由系统应用发出,例如息屏android.intent.action.SCREEN_OFF。非系统应用发送这些广播会报异常。同时非系统应用在发送AppWidgetManager.ACTION_APPWIDGET_CONFIGURE或AppWidgetManager.ACTION_APPWIDGET_UPDATE广播时,只能发给自己,这些是操作自己的桌面小组件。
-
如果当前广播Action属于系统的隐式广播一种,例如ACTION_LOCALE_CHANGED,那么添加上Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND标志,表示广播允许被静态注册的接收者收到。
-
如果广播Action属于系统的一些特殊广播,需要进行一些特别的处理,例如时间变化ACTION_TIME_CHANGED、ACTION_TIMEZONE_CHANGED;应用相关的变化ACTION_PACKAGE_ADDED、ACTION_PACKAGE_DATA_CLEARED、ACTION_PACKAGE_CHANGED等等很多。
以上的逻辑基本是针对系统的一些机制进行特殊逻辑处理,而接下来就是普通广播进行处理,而广播又三种类型,分为黏性、无序、有序广播。
-
黏性广播
-
需要检测发送者是否声明BROADCAST_STICKY权限,且该广播不能携带其他权限检查。
-
不能指定具体的目标组件。即Intent不能设置setComponent函数。
-
非全局的黏性广播不能与已存在全局的黏性广播相同(冲突)。
-
黏性广播的处理:
从mStickyBroadcasts获取当前用户所有的黏性广播stickies,我们知道,广播注册时会从mStickyBroadcasts中取出黏性广播发给广播队列进行处理。根据当前黏性广播的action在stickies映射中查询,查看是否有相同action的黏性广播(list集合),如果有且IntentFilter相同,则替代旧的,不相同则添加到list中。也就是说黏性广播发送只会添加到黏性广播容器中,在广播接收者注册时候才被处理。
-
接下来,与当前广播Intent匹配的广播接收者,静态注册会被收集到receivers集合中(FLAG_RECEIVER_REGISTERED_ONLY没有设置),动态注册的会被收集到registerReceiver容器。
-
无序广播
如果当前广播属于普通,无序的,且registerReceiver集合有收集到匹配的接收者。
-
如果广播发送者是系统应用,需要调用checkBroadcastFromSystem函数检测一下广播的action,做一些警告。
-
根据Intent的flags获取当前广播属于前台广播队列或后台广播队列。
-
广播相关信息封装成BroadcastRecord对象,添加到广播队列的并行集合中mParallelBroadcasts,表示接收者之间可以同时处理该广播。
-
调用广播队列BroadcastQueue的`scheduleBroadcastsLocked`函数。
-
那么接下来,还有静态注册的广播接收者(假如有的话)和有序广播没有处理。在处理完无序广播之后,registerReceiver会被清空。根据所有接收者(receivers和registerReceiver)的优先权合并到同一个同一个队列中,优先权高的放在前面。这里面有几种情况:
-
如果广播是无序广播,那么不会有合并到同一个队列的动作,因为有判断条件。这时候,接下来处理的是无序广播被静态注册接收者处理的部分。
-
如果广播是有序广播,那么不会执行无序广播的处理逻辑,receivers和registerReceiver被合并在一起。然后就是下面的有序广播处理逻辑。
-
有序广播
-
如果广播发送者是系统应用,需要调用checkBroadcastFromSystem函数检测一下广播的action,做一些警告。
-
根据Intent的flags获取当前广播属于前台广播队列或后台广播队列。
-
广播相关信息封装成BroadcastRecord对象,添加到广播队列的顺序集合中mOrderedBroadcasts。
-
调用广播队列BroadcastQueue的`scheduleBroadcastsLocked`函数。
-
也就是说AMS的broadcastIntentLocked函数主要处理一些特殊的Action。然后根据广播类型:黏性、无序、有序,添加到队列的不同集合中,然后调用广播队列BroadcastQueue的scheduleBroadcastsLocked函数对它们进一步处理。
而广播队列分三种类型:前台(优先级最高)、后台(普通优先级)、长广播(耗时很长,例如开机广播)。
特殊Flags处理:
-
默认会给广播Intent对象添加上Intent.FLAG_EXCLUDE_STOPPED_PACKAGES,表示广播不发送给处于停止状态(没有在运行)的BroadcastReceiver。意味着正常情况下,如果我们程序被退出,将无法接收任何广播。
-
FLAG_RECEIVER_REGISTERED_ONLY表示广播只能被动态注册的接收者接收。
2、广播队列对广播的处理
scheduleBroadcastsLocked函数调用mHandler对象发送一个BROADCAST_INTENT_MSG消息。
//BroadcastQueue xxm
public void scheduleBroadcastsLocked() {
if (mBroadcastsScheduled) {//避免重复执行
return;
}
//BroadcastHandler
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
BroadcastHandler的handleMessage函数对BROADCAST_INTENT_MSG消息的处理,直接调用了processNextBroadcast函数。
#BroadcastHandler xxm
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
processNextBroadcast(true);//执行该函数
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
private void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
processNextBroadcastLocked(fromMsg, false);
}
}
processNextBroadcastLocked函数第一步是处理无序广播,即将mParallelBroadcasts列表中的广播发送给它们的接收者进行处理,这里调用了deliverToRegisteredReceiverLocked函数。
//Broadcast对象r来自mParallelBroadcasts集合,N即为receivers长度
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
deliverToRegisteredReceiverLocked(r,
(BroadcastFilter) target, false, i);
}
第二步是对有序广播进行处理:
1. 根据mPendingBroadcast==null来判断当前是否正在等待接收者进程完成启动来处理广播。mPendingBroadcast会在后续进程后被赋值。如果mPendingBroadcast不为null,且进程活着,则继续等待,也就是说,当进程启动完毕之后,处理完广播后,在某个地方会重置mPendingBroadcast,并走到此处。如果进程死亡,则指向下一个接收者,重置mPendingBroadcast。
2. 获取下一个广播,getNextBroadcastLocked函数获取。
优先闹钟广播,接着到期广播,最后有序广播。如果没有获取到广播,调整odj和回收一些资源,那么执行到此处就结束了。
final long now = SystemClock.uptimeMillis();
//优先获取闹钟广播,接着到期广播,最后有序广播
r = mDispatcher.getNextBroadcastLocked(now);//获取下一个BroadcastRecord
if (r == null) {//没有广播需要处理
...
return;
}
3. 丢弃早期的超时广播,即当前时间已经超过了广播约定的处理时间2 * mConstants.TIMEOUT * numReceivers,那么就结束强行结束该广播。
//当前时间超过了广播约定的处理时间,结束该广播
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
//最终调用finishReceiverLocked,参考广播的结束
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
4. 如果广播BraodcastRecord对象没有接收者(receivers ==null),或接收者都已经处理完毕或跳过(nextReceiver >= numReceivers),或者广播被放弃resultAbort、或因为超时被强制丢弃forceReceive,都判断为广播在当前receiver处理结束,调用performReceiveLocked函数。也就意味着一个有序广播处理结束。
//广播的默认状态是IDLE,也就是位对广播处理
if (r.state != BroadcastRecord.IDLE) {
return;
}
//四个条件来判断广播在所有的receiver中处理完毕
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// Send the final result if requested
if (r.resultTo != null) {
boolean sendResult = true;
// splitToken是用于处理延期广播的一个特殊计数
if (r.splitToken != 0) {
int newCount = mSplitRefcounts.get(r.splitToken) - 1;
if (newCount == 0) {
mSplitRefcounts.delete(r.splitToken);
} else {
sendResult = false;
mSplitRefcounts.put(r.splitToken, newCount);
}
}
if (sendResult) {
if (r.callerApp != null) {
mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
r.callerApp);
}
try {
//广播下个处理的地方
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
} catch (RemoteException e) {
r.resultTo = null;
}
}
}
//将广播添加到历史消息
cancelBroadcastTimeoutLocked();
addBroadcastToHistoryLocked(r);
if (r.intent.getComponent() == null && r.intent.getPackage() == null
&& (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
}
mDispatcher.retireBroadcastLocked(r);
r = null;
looped = true;
continue;
}
如果当前需要将结果传递给调用者,那么需要调用performReceiveLocked函数。
5. Broadcast对象deferred属性为true时,说明当前广播是从延期广播数组获取的,不需要再进行延期判断。为false时,判断当前广播后续接收者的进程对广播是否需要延期处理(如果之前处理广播所花费的时间超过约定时间,该进程会被记录)、
//在被加入延期广播的时候为true,默认为false
if (!r.deferred) {
//广播下一个接收者的进程uid
final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
//根据uid在mDeferredBroadcasts列表获取是否有延期的广播
if (mDispatcher.isDeferringLocked(receiverUid)) {
BroadcastRecord defer;
if (r.nextReceiver + 1 == numReceivers) {//只有一个接收者,不需要设计splitToken
defer = r;
mDispatcher.retireBroadcastLocked(r);
} else {//多个接收者
//splitRecipientsLocked函数会检查当前广播剩下的receiver,获取于当前receiver相同进程的receiver列表,新建broadcast对象,把receiver列表加到该对象中,也就是这里的defer
defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
// Track completion refcount as well if relevant
if (r.resultTo != null) {
int token = r.splitToken;
if (token == 0) {
// 第一次给广播设计splitToken,本质了i++。叠加mSplitRefcounts计数
r.splitToken = defer.splitToken = nextSplitTokenLocked();
mSplitRefcounts.put(r.splitToken, 2);
} else {
//后续只需要增加mSplitRefcounts计数
final int curCount = mSplitRefcounts.get(token);
mSplitRefcounts.put(token, curCount + 1);
}
}
}
//将广播添加到延期队列中,这里deferred会被设置为true;
mDispatcher.addDeferredBroadcast(receiverUid, defer);
r = null;
looped = true;
continue;
}
}
第2到第4,是在一个do-while循环,退出条件是r!=null,即找到下个要处理广播对象。在第2,是从闹钟广播队列、延期广播队列、顺序广播队列获取即将要执行的广播,如果没有广播,会直接跳出本函数;第4是当前广播处理结束,执行结束流程;第5是广播转入到延期广播队列中。经过前面的步骤,能获取到广播对象,则进入下面第6。
6. 获取broadcast下个接收者receiver。广播的receiverTime和dispatchTime在此被记录,会影响广播处理时间的计算,可能会导致receiver的进程加入延期队列,被特殊对待。
• 是BroadcastFilter对象,说明是动态注册的接收者,直接调用deliverToRegisteredReceiverLocked,与无序广播处理逻辑一致。
• 是ResolveInfo对象。说明是静态注册的,创建目标组件对象ComponentName。
7. 在第6步情况下,receiver是ResolveInfo对象,需要进行毕竟繁琐复杂的逻辑处理。
• 进行权限检查,这部分内容很多,感兴趣的朋友可以自己翻阅看看,主要是对系统的一些机制,广播发送者、接收者的合法权限检查。
• 进程已启动,执行processCurBroadcastLocked函数。该函数处理调到后面第3小节。
• 进程未启动,执行AMS.startProcessLocked启动进程,如果启动进程失败,需要结束接收者(参考广播的结束),执行下个广播处理(即处理本小结所有内容)。mPendingBroadcast被设置为当前广播对象,于开头等待广播所在进程启动完毕相照应。
回到第一步的无序广播处理中,调用deliverToRegisteredReceiverLocked函数主要是对权限进行处理,与前面的权限检查大致相同,然后调用了performReceiveLocked函数,与有序广播在第4对结束处理逻辑一致。
在performReceiveLocked函数中,如果receiver所在的进程已经启动,直接调用 app.thread.scheduleRegisteredReceiver异步方式调用,这样保证广播Intent回到receiver所在进程进行处理。如果app未启动,则以同步方式调用 receiver.performReceive。
//BroadcastQueue xxm
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
//异步方式调用receiver所在线程scheduleRegisteredReceiver函数
if (app != null) {
if (app.thread != null) {
...
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
...
} else {
//同步方式
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
异步方式,我们定位到ApplicationThread的scheduleRegisteredReceiver函数。
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);
}
所以,无论异步方式还是同步方式调用,最终还是回调LoadedApk.ReceiverDispatcher.InnerReceiver类的performReceive函数。
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
if (intent == null) {
rd = null;
} else {
rd = mDispatcher.get();
}
if (rd != null) {
//调用performReceive
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {//Intent为null,finish掉receiver,逻辑与解注册unregisterReceiver函数相似
IActivityManager mgr = ActivityManager.getService();
try {
if (extras != null) {
extras.setAllowFds(false);
}
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
ReceiverDispatcher的performReceive函数
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
...
//分析2
if (intent == null || !mActivityThread.post(args.getRunnable())) {
if (mRegistered && ordered) {//顺序广播
IActivityManager mgr = ActivityManager.getService();
args.sendFinished(mgr);//通知结束
}
}
}
ReceiverDispatcher.performReceive函数中将相关数据封装成Args对象,Args继承自PendingResult,代表了广播的处理结果。注意分析1,if语句,Args的getRunnable函数返回了Runnable对象,也就说这里执行了Runnable对象的run函数。这时切换到应用进程的主线程。
Args.getRunnable函数调用了BroadcastReceiver的onReceive函数,即我们的业务逻辑。接着就是调用finish函数,执行广播的结束操作,这部分可以参考后文广播的结束。
public final Runnable getRunnable() {
return () -> {
//自定义的BroadcastReceiver对象
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
final IActivityManager mgr = ActivityManager.getService();
final Intent intent = mCurIntent;
mCurIntent = null;
mDispatched = true;
mRunCalled = true;
//广播异常、接收者异常、丢弃
if (receiver == null || intent == null || mForgotten) {
if (mRegistered && ordered) {
sendFinished(mgr);//有序广播的回调
}
return;
}
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent,
mContext.getAttributionSource());
setExtrasClassLoader(cl);
//设置了处理结果
receiver.setPendingResult(this);
//回调onReceive函数
receiver.onReceive(mContext, intent);
} catch (Exception e) {
//异常,有序广播通知结束
if (mRegistered && ordered) {
sendFinished(mgr);
}
...
}
//onReceiver函数前设置了
if (receiver.getPendingResult() != null) {
finish();
}
};
}
3、processCurBroadcastLocked
在第2步中,有序广播在对Receiver进行处理的时候,如果进程已经启动,会调用processCurBroadcastLocked函数。该函数进又调用了ApplicationThread的scheduleReceiver函数。
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
final IApplicationThread thread = app.getThread();
...
r.receiver = thread.asBinder();
r.curApp = app;
final ProcessReceiverRecord prr = app.mReceivers;
prr.addCurReceiver(r);
...
// Tell the application to launch this receiver.
r.intent.setComponent(r.curComponent);
boolean started = false;
try {
...
thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.mState.getReportedProcState());
started = true;
} finally {
if (!started) {
r.receiver = null;
r.curApp = null;
prr.removeCurReceiver(r);
}
}
}
ApplicationThread的scheduleReceiver函数将相关信息封装成了ReceiverData类型的对象。ReceiverData继承自PendingResult,也代表了广播在当前BroadcastReceiver的处理结果,广播结束会调用它的finish函数。后面再看看finish函数做了什么。接着给ActivityThread的H对象发RECEIVER消息。
//这里的info是通过Resolver解析出来的,存储了BroadcastReceiver信息
//sync顺序与无序==同步与异步
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);
}
H.handleMessage的RECEIVER分支调用了ActivityThread的handleReceiver函数,也就说,现在切回到应用的主线程。函数中通过packageInfo.getAppFactory().instantiateReceiver函数通类加载机制创建了BroadcastReceiver对象,也就是我们自定义的广播接收者,接着回调了onReceive函数,将Intent对象传递给我们,广播处理流程也到此结束了。
private void handleReceiver(ReceiverData data) {
...
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManager.getService();
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
app = packageInfo.makeApplication(false, mInstrumentation);
context = (ContextImpl) app.getBaseContext();
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
final String attributionTag = data.info.attributionTags[0];
context = (ContextImpl) context.createAttributionContext(attributionTag);
}
//类加载机制所必须的信息
java.lang.ClassLoader cl = context.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess(
isProtectedComponent(data.info) || isProtectedBroadcast(data.intent),
context.getAttributionSource());
data.setExtrasClassLoader(cl);
//通过类加载机制创建我们的BroadcastReceiver对象,packageInfo.getAppFactory返回AppComponentFactory对象
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
data.sendFinished(mgr);
}
try {
//线程内的全局单例,应该在某个地方会读取判断
sCurrentBroadcastIntent.set(data.intent);
//设置处理结果
receiver.setPendingResult(data);
//回调我们自定义的onReceive方法。
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
data.sendFinished(mgr);
} finally {
sCurrentBroadcastIntent.set(null);
}
//前面默认设置了data,所以默认情况接收处理,如果不想广播结束,再次调用setResult
if (receiver.getPendingResult() != null) {
data.finish();
}
}
//AppComponentFactory
public @NonNull BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (BroadcastReceiver) cl.loadClass(className).newInstance();
}
广播的结束
按照前面的分析,广播Intent传递给接收者的onReceive
函数之后,都会调用PendingRsult
的finish
函数来结束当前广播。
如果程序当前工作队列还有要执行的工作,则添加一个执行sendFinished函数任务到队列,排队等待执行。否则就立即执行sendFinished函数。
//PendingRsult
public final void finish() {
if (mType == TYPE_COMPONENT) {//ReceiverData创建时,被设置为TYPE_COMPONENT,Args创建时TYPE_REGISTERED
final IActivityManager mgr = ActivityManager.getService();
//QueuedWork用于跟踪应用全局的工作是否结束
if (QueuedWork.hasPendingWork()) {
QueuedWork.queue(new Runnable() {
@Override public void run() {
sendFinished(mgr);
}
}, false);
} else {
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {//mOrderedHint为true表示顺序广播,TYPE_UNREGISTERED表示当前结注册
final IActivityManager mgr = ActivityManager.getService();
sendFinished(mgr);
}
}
如果当前广播已经执行过结束流程,那么mFinished设置为true,再次执行就会抛出异常。接着根据广播的类型是有序还是无序,传递不同参数给ActivityManagerService的finishReceiver函数,通知AMS结束广播。
public void sendFinished(IActivityManager am) {
synchronized (this) {
if (mFinished) {
throw new IllegalStateException("Broadcast already finished");
}
mFinished = true;
try {
if (mResultExtras != null) {
mResultExtras.setAllowFds(false);
}
if (mOrderedHint) {//有序
am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast, mFlags);
} else {//无序
am.finishReceiver(mToken, 0, null, null, false, mFlags);
}
} catch (RemoteException ex) {
}
}
}
在广播的解注册,我们分析过AMS的unregisterReceiver函数,与这里的finishReceiver函数类似。根据广播Intent的flags查询广播所在队列,然后再广播队列中查出当前广播BroadcastRecord对象,调用其finishReceiverLocked函数,结束掉当前广播,如果还有其他接收者在等待,调用processNextBroadcastLocked函数,执行下一个广播。
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
// Refuse possible leaked file descriptors
if (resultExtras != null && resultExtras.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Bundle");
}
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
BroadcastQueue queue;
synchronized(this) {
//根据Intent的Flags获取广播队列
if (isOnOffloadQueue(flags)) {
queue = mOffloadBroadcastQueue;
} else {
queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
}
//获取当前正在处理的广播,也就是现在接收者对应的广播
r = queue.getMatchingOrderedReceiver(who);
if (r != null) {
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
if (doNext) {//如果有,执行下一个广播。参考=>广播队列对广播的处理小节
r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
}
// updateOomAdjLocked() will be done here
trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
finishReceiverLocked函数主要是对Broadcast和BroadcastReceiver做一些清场工作,同时根据本次广播处理时间在核心系统应用做一些优化策略。同时也有可能需要等待下一个程序的Receiver来执行当前广播,也就是说广播只是在当前程序的接收者处理完,可能还有下个程序也要处理。
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final int state = r.state;
final ActivityInfo receiver = r.curReceiver;
final long finishTime = SystemClock.uptimeMillis();
final long elapsed = finishTime - r.receiverTime;
r.state = BroadcastRecord.IDLE;
if (r.allowBackgroundActivityStarts && r.curApp != null) {
if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
//执行时间超过允许后台Activity启动时间,则没有启动,移除token即可
r.curApp.removeAllowBackgroundActivityStartsToken(r);
} else {
//赋予更多的时间用来启动后台Activity
postActivityStartTokenRemoval(r.curApp, r);
}
}
//记录执行时间
if (r.nextReceiver > 0) {
r.duration[r.nextReceiver - 1] = elapsed;
}
// 广播在当前程序执行缓慢,非核心系统app下次处理广播时启动延迟策略
//timeoutExempt为true表示不受超时影响,默认为false
if (!r.timeoutExempt) {
if (r.curApp != null
&& mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
if (!UserHandle.isCore(r.curApp.uid)) {
//将进程添加到缓慢列表中
mDispatcher.startDeferring(r.curApp.uid);
}
}
}
//当前广播的清场工作
r.receiver = null;
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
r.curApp.mReceivers.removeCurReceiver(r);
mService.enqueueOomAdjTargetLocked(r.curApp);
}
if (r.curFilter != null) {
r.curFilter.receiverList.curBroadcast = null;
}
r.curFilter = null;
r.curReceiver = null;
r.curApp = null;
mPendingBroadcast = null;
r.resultCode = resultCode;
r.resultData = resultData;
r.resultExtras = resultExtras;
if ( && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
r.resultAbort = resultAbort;
} else {
r.resultAbort = false;
}
// 其他接收者正在等待当前接收者处理完
if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
&& r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
ActivityInfo nextReceiver;
if (r.nextReceiver < r.receivers.size()) {
Object obj = r.receivers.get(r.nextReceiver);
nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
} else {
nextReceiver = null;
}
//相同进程的receiver不会执行到此处
if (receiver == null || nextReceiver == null
|| receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
|| !receiver.processName.equals(nextReceiver.processName)) {
//有后台服务,表示当前正在切换不同的程序的receiver来处理当前broadcast
if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
r.state = BroadcastRecord.WAITING_SERVICES;
return false;
}
}
}
r.curComponent = null;
// We will process the next receiver right now if this is finishing
// an app receiver (which is always asynchronous) or after we have
// come back from calling a receiver.
return state == BroadcastRecord.APP_RECEIVE
|| state == BroadcastRecord.CALL_DONE_RECEIVE;
}
总结
广播会发送到广播队列中不同集合。其中广播队列有三种类型,分别对应优先级从高到底:前台、后台、长广播队列类型。而广播又分三种:黏性广播、无序广播、有序广播。无序广播和有序广播的处理主要要发送广播的时候,而黏性广播则在广播接收者注册时候被处理。
一个应用程序允许注册最大的广播接收者是1000个。广播之间的传递也要经历各种权限检查,所以广播不适合在应用间用于频繁的交互。
作者:新小梦
链接:https://juejin.cn/post/7322156751818522661
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。