上一篇分析了Android系统中广播的注册和注销,这一篇我们继续分析发送广播的源码流程,广播的发送相对来说比较复杂,所以需要比较长的时间来看,如果你看懂了流程相对来说再研究流程中的细节就比较简单了。
先看一张时序图,因为里面涉及到循环过程,代码中会提到,但是时序图没有绘制,所以需要注意一下。
0、ContextImpl.sendBroadcast
/**
* 发送广播
* 步骤:
* 1.广播发送者,即一个Activity组件或者一个Service组件,将一个特定类型的广播发送给AMS
*
* 2.AMS接收到一个广播后,首先找到与这个广播对应的广播接收者,然后将它们添加到一个广播调度队列中,
* 最后向AMS所运行在的先从的消息队列发送一个类型为BROADCAST_INTENT_MSG的消息,这时候对广播发送
* 者来说,一个广播就发送完了。
*
* 3.当发送到AMS所运行在的线程的消息队列中的BROADCAST_INTENT_MSG消息被处理时,AMS就会从广播调
* 度队列中知道需要接受广播的接收者,并且将对应的广播发送给它们所运行在的应用进程。
*
* 4.广播接收者所运行的应用程序接收到AMS发送过来的广播后,并不是直接将接收到的广播发送给广播接收
* 者来处理,而是将接收到的广播封装成一个消息,并且发送到主线程的消息队列中。当找个消息被处理时,
* 应用程序进程才会将它所描述的广播发给相应的广播接收者处理
*
* 参考:
* http://blog.csdn.net/houliang120/article/details/51607170
* http://gityuan.com/2016/06/04/broadcast-receiver/
* http://blog.csdn.net/windskier/article/details/7251742
*
* @param intent The Intent to broadcast; all receivers matching this
* Intent will receive the broadcast.
*/
@Override
public void sendBroadcast(Intent intent) {
...
try {
...
// 调用ActivityManagerProxy中的broadcastIntent,然后通过Binder调用ActivityManagerService
// 中的broadcastIntent
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的broadcastIntent方法,然后通过Binder调用ActivityManagerService中的对应方法。
1、ActivityManagerProxy.broadcastIntent
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 {
...
// 通过Binder对象mRemote向AMS发送一个类型为BROADCAST_INTENT_TRANSACTION的进程间通信请求
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
...
return res;
}
复制代码
2、ActivityManagerService.broadcastIntent
// 发送广播
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized (this) {
// 验证广播的Intent是否合法,如果这个时候系统正在启动,还会验证intent所描述的广播是否只发送
// 给动态注册的广播接收者。在系统的启动过程中,PMS可能还未启动,这种情况下,AMS是无法获取静态
// 注册广播接收者的,因此,就禁止发送广播给静态注册的广播接收者
intent = verifyBroadcastLocked(intent);
// 根据caller从缓存mLruProcesses中获取进程对象ProcessRecord
final ProcessRecord callerApp = getRecordForAppLocked(caller);
...
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
复制代码
3、ActivityManagerService.broadcastIntentLocked
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
// 增加下面flag,默认不发送广播到已经停止的app
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
// If we have not finished booting, don't allow this to launch new processes.
// 如果该进程还没有完成启动,并且不是发送给启动升级的广播,则添只发送给已注册的广播接收者标签
if (!mProcessesReady && (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);// 只发给注册的receiver
}
...
// 获取当前发送广播应用所在用户的userId
// 解释:
// 7.1.1系统有多用户登录,就像电脑上的主用户和访客模式一样,可以设置多个用户,每个用户有一个id,
// 有些广播需要多个用户都要接收,比如时区变化这些,是同步的,每个用户都要变化。这里一般是我们
// 当前登录的用户id
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_NON_FULL, "broadcast", callerPackage);
// Make sure that the user who is receiving this broadcast is running.
// If not, we will just skip it. Make an exception for shutdown broadcasts
// and upgrade steps.
if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}
...
// Verify that protected broadcasts are only being sent by system code,
// and that system code is only sending protected broadcasts.
final String action = intent.getAction();
final boolean isProtectedBroadcast;
try {
// 验证是不是受保护的广播(是不是系统广播)
isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
} catch (RemoteException e) {
...
return ActivityManager.BROADCAST_SUCCESS;
}
// 检查是不是系统调用
final boolean isCallerSystem;
switch (UserHandle.getAppId(callingUid)) {
case Process.ROOT_UID:
case Process.SYSTEM_UID:
case Process.PHONE_UID:
case Process.BLUETOOTH_UID:
case Process.NFC_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = (callerApp != null) && callerApp.persistent;
break;
}
// First line security check before anything else: stop non-system apps from
// sending protected broadcasts.
if (!isCallerSystem) {// 不是系统发送的广播
if (isProtectedBroadcast) {// 非系统进程发送受保护广播抛出异常
throw new SecurityException(msg);
} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
|| AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
// 如果是配置小部件或者升级小部件的广播
// Special case for compatibility: we don't want apps to send this,
// but historically it has not been protected and apps may be using it
// to poke their own app widget. So, instead of making it protected,
// just limit it to the caller.
if (callerPackage == null) {
throw new SecurityException(msg);
} else if (intent.getComponent() != null) {
// They are good enough to send to an explicit component... verify
// it is being sent to the calling app.
if (!intent.getComponent().getPackageName().equals(
callerPackage)) {
throw new SecurityException(msg);
}
} else {
// Limit broadcast to their own package.
// 限制发送广播给自己包里
intent.setPackage(callerPackage);
}
}
}
// 下面主要是针对系统广播的处理
if (action != null) {
switch (action) {
case Intent.ACTION_UID_REMOVED:// 移除uid
case Intent.ACTION_PACKAGE_REMOVED:// 卸载应用
case Intent.ACTION_PACKAGE_CHANGED:// 应用更改,比如:停用,启动等
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// 外部应用不可用,比如安装到sd卡的应用,卸载了sd卡
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:// 外部应用可用
case Intent.ACTION_PACKAGES_SUSPENDED:// 暂停应用
case Intent.ACTION_PACKAGES_UNSUSPENDED:// 应用可用
switch (action) {
case Intent.ACTION_UID_REMOVED:// 移除系统userId(删除一个用户)
...
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// 外部应用不可用,一般是卸载SD卡
...
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:// 外部应用可用,一般是插入SD卡
...
break;
case Intent.ACTION_PACKAGE_REMOVED:// 卸载
case Intent.ACTION_PACKAGE_CHANGED:// 更新
...
break;
case Intent.ACTION_PACKAGES_SUSPENDED:
case Intent.ACTION_PACKAGES_UNSUSPENDED:
...
break;
}
break;
case Intent.ACTION_PACKAGE_REPLACED: {// 升级应用
...
break;
}
case Intent.ACTION_PACKAGE_ADDED: {// 安装应用
...
break;
}
case Intent.ACTION_PACKAGE_DATA_CLEARED: {// 清理应用数据
...
break;
}
case Intent.ACTION_TIMEZONE_CHANGED:// 时区改变
...
break;
case Intent.ACTION_TIME_CHANGED:// 时间改变
...
break;
case Intent.ACTION_CLEAR_DNS_CACHE:// 清理DNS缓存
...
break;
case Proxy.PROXY_CHANGE_ACTION:// 代理改变
...
break;
case android.hardware.Camera.ACTION_NEW_PICTURE:// 新照片
case android.hardware.Camera.ACTION_NEW_VIDEO:// 新视频
...
return ActivityManager.BROADCAST_SUCCESS;
}
}
// Add to the sticky list if requested.
if (sticky) {// 判断是否是粘性广播,如果是,AMS就需要保存这个广播,以便后面注册要接收此类型广播的接收者可以获得这个广播
// 检查粘性广播是否申请了权限
...
if (requiredPermissions != null && requiredPermissions.length > 0) {
...
return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}
// sticky广播不能指定目标组件
...
// We use userId directly here, since the "all" target is maintained(维护)
// as a separate set of sticky broadcasts.
if (userId != UserHandle.USER_ALL) {// 不是发送给所有用户的广播
// But first, if this is not a broadcast to all users, then
// make sure it doesn't conflict(冲突) with an existing broadcast to
// all users.
ArrayMap> stickies = mStickyBroadcasts.get(
UserHandle.USER_ALL);
...// 检查是否和存在的发给所有用户的粘性广播一样的广播
}
// 在AMS中所有的粘性广播都保存在一个列表中,这些列表最终保存在AMS的成员变量mStickyBroadcasts
// 所描述的一个HashMap中,并且以它们的广播类型为关键字
// 首先检查mStickyBroadcasts是否有改用户的粘性广播列表
ArrayMap> stickies = mStickyBroadcasts.get(userId);
// 该广播列表中没有该用户的stick广播列表
if (stickies == null) {
stickies = new ArrayMap<>();
mStickyBroadcasts.put(userId, stickies);
}
// 获取注册广播的Action对应的粘性广播的Intent列表
ArrayList list = stickies.get(intent.getAction());
if (list == null) {// 如果为空
list = new ArrayList<>();// 创建一个列表
stickies.put(intent.getAction(), list);// 以action为键保存该列表
}
// 获取该action对应粘性广播Intent列表的个数
final int stickiesCount = list.size();
int i;
// 检查在粘性广播列表中是否保存了一个与参数Intent一致的广播。如果存在直接替换,否则将参数
// Intent描述的广播添加到粘性广播列表list中
for (i = 0; i < stickiesCount; i++) {
if (intent.filterEquals(list.get(i))) {
// This sticky already exists, replace it.
list.set(i, new Intent(intent));
break;
}
}
if (i >= stickiesCount) {// 如果该列表中不存在该粘性广播的Intent加入进去
list.add(new Intent(intent));
// 我们看到粘性广播放在了list中,而list以action为键放置在了stickies中,而stickies
// 又以userId为键放在了mStickyBroadcasts中,因此mStickyBroadcasts保存了设备中所有
// 用户粘性广播的Intent
}
}
int[] users;
if (userId == UserHandle.USER_ALL) {// 发送广播给全部用户
// Caller wants broadcast to go to all started users.
// 获取所有已启动用户的列表
users = mUserController.getStartedUserArrayLocked();
} else {// 发送广播给指定用户
// Caller wants broadcast to go to one specific user.
users = new int[]{userId};
}
// Figure out who all will receive this broadcast.
List receivers = null;// 静态注册接收者
List registeredReceivers = null;// 动态注册接收者
// Need to resolve the intent to interested receivers...
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {// 如果当前的广播Intent没有指定FLAG_RECEIVER_REGISTERED_ONLY标记,也就是允许静态注册
// 允许静态注册的Intent,需要从PMS中去查询对应的广播接收者
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
// 如果参数intent没有指定广播接收者的组件名,说明是发送给所有已注册并且要接收该广播的接收者的
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;
}
// registeredReceiver快结束前将注册的BroadcastFilter放入mReceiverResolver中
// 里面包含了对应的动态注册的广播接收者
List 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);
}
}
// 由于AMS是通过消息机制将接收到的广播发送给目标广播接收者的,因此可能会出现一种情况:上次接收
// 的广播还没有来得及发送给广播接收者,又马上接收到一个同样的广播,在这种情况下,如果现在接收的
// 广播标志位FLAG_RECEIVER_REPLACE_PENDING等于1,那么AMS就会用新的广播代替旧的广播。
final boolean replacePending =
(intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
// 动态广播接收者个数
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
// 处理无序广播并且存在动态接收者,首先将当前发送的广播,即参数Intent所描述的对象转发给这些动态
// 注册的目标广播接收者,然后转发给静态广播接收者
if (!ordered && NR > 0) {
...
// 根据intent查找对应的广播队列(前台优先级队列还是后台优先级队列),创建BroadcastRecord
final BroadcastQueue queue = broadcastQueueForIntent(intent);
// 将参数intent所描述的广播以及动态注册的目标广播接收者封装成一个BroadcastRecord对象r,
// 用来描述AMS要执行的一个广播转发任务
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
resultExtras, ordered, sticky, false, userId);
...
// 在BroadcastQueue中等待发送广播中搜索是否有相同的BroadcastRecord并且是否替换
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
// 如果不需要替换则插入到BroadcastQueue中,并推动一次广播发送
// ------------------!!!重要!!!-------------这里如果是非有序广播,那就都是动态广播接收者
// 也就是说动态广播接收者都放在了BroadcastQueue的mParallelBroadcasts中
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// Merge into one list.动态广播registeredReceivers和静态广播receivers合并
// 如果是order广播,动态接收者和静态的接收者合并到一个队列里面进行处理,也就是说order广播下,
// 所有的接收者(静态和动态)处理方式都是一样的(后面会分析到,都是串行化处理的)。
// 还有就是对于静态的接收者而言,始终是和order广播的处理方式是一样的,也就是说静态的接收者
// 只有order模式(串行化接收)。
// 在合并过程中,如果一个动态注册的广播接收者和一个静态注册的目标广播接收者的优先级相同,那么
// 动态注册的目标接收者会排在静态注册的目标广播接收者前面,即动态注册的目标广播接收者会优先于
// 静态注册的广播接收者接受有序广播
int ir = 0;
if (receivers != null) {// order广播(静态广播)
// 对于ACTION_PACKAGE_ADDED广播而言,如果是自己被add了,那么这个广播只能别人收到,
// 自己即使注册了这个静态广播也接收不到,注释上说是担心有些应用一安装就接收自己的PACKAGE_ADDED
// 广播,然后就启动了。简言之,应用永远接收不到自己的PACKAGE_ADDED广播。
String skipPackages[] = null;// 需要跳过的广播
...
// 合并中...
// 下面这一段代码是典型的有序链表的合并操作,合并的依据是接收者的priority值,这里需要注意的一点
// 是动态广播接收器注册的时候一般都没有指定priority,默认值是0,具体实现好像有点像归并排序的意思,
// 而且是从后向前进行的归并,因此priority越小,在链表中的位置就越靠前,后面处理的时候也就越先处理。
// 走完这里所有的静态接收者和order模式下的动态接收者都已经被合并到了receivers链表里面。
// 合并以后在receivers链表里面静态接收者对应ResolveInfo对象,order模式下的动态接收者对应的
// 是BroadcastFilter对象。
int NT = receivers != null ? receivers.size() : 0;// 静态注册广播接收者
int it = 0;
ResolveInfo curt = null;// 静态接收者
BroadcastFilter curr = null;// 动态接收者
...
}
// 对于无序广播来说,静态注册的广播接收者全部保存在列表receivers中,而对于有序广播来说,静态
// 注册和动态注册的目标广播接收者全部保存在receivers列表中
// 合并中...
...
// 可以看出,是在合并入receiver后统一发送BroadcastQueue.scheduleBroadcastsLocked
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
// 将剩余的其他目标广播接收者封装成另外的一个BroadcastRecord对象,用来描述AMS要执行的另
// 一个广播转发任务,兵器添加到AMS内部的有序广播调用队列中
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
// 在BroadcastQueue中等待发送广播中搜索是否有相同的BroadcastRecord并且是否替换
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {// 存在需要AMS的有序广播调用队列中增加新的额广播任务
// ---------!!!重要!!!----------包含动态和静态接收者
// 也就是说静态广播都放在BroadcastQueue的mOrderedBroadcasts中,这里也有orderd动态广播
queue.enqueueOrderedBroadcastLocked(r);
// 执行发送广播
queue.scheduleBroadcastsLocked();
}
} else {
// There was nobody interested in the broadcast, but we still want to record
// that it happened.
if (intent.getComponent() == null && intent.getPackage() == null
&& (intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
复制代码
这里代码比较多,简单解释一下,首先是判断是不是系统广播,也就是switch语句中的部分,这部分的广播是系统发出的,根据不同广播做出不同的处理,系统广播我们可以接收但是不能发送,只能由系统发出,详细的不再解释,自己可以看看;然后是sticky广播的处理;然后是静态广播和动态广播的处理,在动态广播和静态广播处理中,先处理动态广播接收者,再处理静态广播接收者,因此动态广播接收者先收到广播,然后是静态广播接收者,动态广播和静态广播接收者都会通过调用BroadcastQueue.scheduleBroadcastsLocked方法来发送广播,在看这个代码之前先看一下时序图中的第四步。
4、AMS.broadcastQueueForIntent
// 根据Intent所带标签判断接收者是以前台优先级还是后台优先级运行,前台接收者优先级超时时间较短
// 正常是后台优先级运行,并且不会被提升到前台优先级
BroadcastQueue broadcastQueueForIntent(Intent intent) {
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
复制代码
这个方法主要是根据Intent中的标签在判断是接收者是后台优先级还是前台优先级,关于这两个概念看上面代码中注释的内容。
5、BroadcastQueue.scheduleBroadcastsLocked
// 驱动广播,所有广播都应该从这里走,然后会到processNextBroadcast
public void scheduleBroadcastsLocked() {
...
// mBroadcastsScheduled用来描述AMS是否已经向它所运行在的线程的消息队列发送了一个类型为
// BROADCAST_INTENT_MSG的消息。AMS就是通过这个BROADCAST_INTENT_MSG消息类调度保存在无
// 序广播调度队列mParallelBroadcasts和有序广播调度队列mOrderedBroadcasts中的广播转发任务的
if (mBroadcastsScheduled) {// 如果true说明消息队列已经存在一个类型为BROADCAST_INTENT_MSG的消息了
return;
}
// 虽然这里只发送了发送广播的消息,但是这一步执行完之后就已经标记广播发送了,因此可以看出广播发送和接
// 受是异步的,即广播发送者将一个广播发送给AMS后,不会等待AMS将这个广播转发给广播接收者处理
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
复制代码
广播中大多数内容的处理是在BroadcastQueue类中,上面有个mBroadcastsScheduled参数,如果为ture则阻止继续执行,那么我们看到下面发完消息后会将其设置为true,我们接着看哪里将其执行为false的,
6、BroadcastQueue.processNextBroadcast
// 广播的核心部分,参数fromMsg是用来描述AMS类的成员函数processNextBroadcast是否是用来处理类型为
// BROADCAST_INTENT_MSG的消息的
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
BroadcastRecord r;
...
if (fromMsg) {// 从msg过来的时候为true
// 前面说到,如果消息队列里面有BROADCAST_INTENT_MSG消息,该标记为true,
// 阻止新的消息加入队列,这里开始处理这个消息的时候,将mBroadcastsScheduled变量设置为false,
// 开始允许新的消息加入。
mBroadcastsScheduled = false;
}
// 无序广播之间不存在相互等待,这里处理的是所有非order的动态广播
// 处理保存在无序广播调度队列mParallelBroadcasts中的广播发送任务,即把保存在无序广播调度
// 队列mParallelBroadcasts中的广播发送给它的目标广播接收者处理
while (mParallelBroadcasts.size() > 0) {
// 首先保存无序广播调度队列mParallelBroadcasts中的每一个BroadcastRecord对象
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
// 调用deliverToRegisteredReceiverLocked向所有的receivers发送广播
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);
}
...
// 接下来处理保存在有序广播调度队列mPendingBroadcast中的广播转发任务。有前面可知,有序广播
// 调度队列mOrderedBroadcast描述的目标广播接收者有可能是静态注册的,而这些静态注册的目标广
// 播接收者可能还没有启动起来,因此AMS将一个广播发送给它们处理时,首先将它们启动起来。事实上,
// AMS只需要将他们所运行在的进程启动起来就可以了,因为当这些进程接收到AMS发送的广播后,就会
// 主动将目标广播接收者启动起来
// mPendingBroadcast对象是用来描述一个正在等待静态注册的目标广播接收者启动起来的广播转发任务的
if (mPendingBroadcast != null) {
boolean isDead;
synchronized (mService.mPidsSelfLocked) {
// 检查目标广播接收者所在进程是否已经启动
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
if (!isDead) {// 如果正在启动,等待
// It's still alive, so keep waiting
return;
} else {// 已经启动准备发送广播
...
}
}
boolean looped = false;
// 逐条处理有序广播列表mOrderedBroadcasts中的BroadcastRecord
do {
// 判断有序广播调度队列mOrderedBroadcasts是否还有需要处理的广播
if (mOrderedBroadcasts.size() == 0) {// 没有,说明调度队列中的广播已经处理完成
...
return;
}
// 如果没有处理完成,取出下一个马上要处理的广播BroadcastRecord
r = mOrderedBroadcasts.get(0);
boolean forceReceive = false;
// 获取广播转发任务的目标广播接收者的个数
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
// 检查前一个目标广播接收者是否在规定的时间内处理完成AMS给它发送的一个有序广播。AMS处理
// BroadcastRecord对象r所描述的一个广播转发任务时,会将当前时间记录在这个BroadcastRecord
// 对象中,如果这个广播任务不能在(2*BROADCAST_TIMEOUT*numReceivers)毫秒内完成,即它的目
// 标广播接收者不能在(2*BROADCAST_TIMEOUT*numReceivers)毫秒内完成AMS给它们发送的一个有
// 序广播,那么会调用broadcastTimeoutLocked函数来强制结束这个广播转发任务,
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mTimeoutPeriod * numReceivers))) {
...
// 出现超时,强制结束
broadcastTimeoutLocked(false); // forcibly finish this broadcast
// 重置参数,继续处理有序广播调度队列mOrderedBroadcasts的下一个广播转发任务
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
// 检测广播转发任务是否正在处理中,即AMS正在将一个有序广播转发给它的前一个目标广播接收处理者,
// 如果是,AMS就会等待这个目标广播接收者处理完该有序广播,然后再转发给下一个广播接收者处理
...
// 表示广播已经向所有的receiver发送结束或者中途被取消, 如果r.resultAbort为true,会停止处理
// 当前正在发送的BroadcastRecord,这样优先级比较低的接收者也就收不到这个广播了
// 检查BroadcastRecord对象r所描述的广播转发任务是否已经处理完成,或者是否已经被强制结束了。
// 如果是,那么调用函数cancelBroadcastTimeoutLocked来删除前面发送到AMS所运行在的线程的消息
// 队列中的一个BROADCAST_TIMEOUT_MSG消息,表示BroadcastRecord对象r所描述的广播转发任务已经
// 在规定时间内处理完成了。接下来就讲改广播BroadcastRecord对象从队列中删除,然后赋值为null
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// No more receivers for this broadcast! Send the final
// result if requested...
if (r.resultTo != null) {
try {
...
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {
...
}
}
...
// ... and on to the next...
...
// BroadcastRecord处理完移除
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);// 如果第一次取出的r不为空,则退出循环
// Get the next receiver...(获取下一个将要处理的广播接收者在其列表中的位置)
int recIdx = r.nextReceiver++;
// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
// 保存当前时间,
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {// 表示第一个开始处理的接收者,也就是BroadcastRecord对象r所描述的广播任务刚被处理
// 接收者开始处理的时间戳,也就是这个接收者开始处理了,要记录开始时间来计算是否超过超时时间
// 也就是说这是BroadcastRecord中第一个接收者开始被处理的时间戳,也就是上面BroadcastRecord
// 超时的起点,可以看到上面超时比较的时候用的就是r.dispatchTime
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
...
}
// 检查AMS是否已经向它所在的线程的消息队列发送了类型为BROADCAST_TIMEOUT_MSG的消息,如果没有发送,
// 那么会调用setBroadcastTimeoutLocked函数向这个线程发送一个类型为setBroadcastTimeoutLocked
// 的消息,并且制定在timeoutTime毫秒后处理。上面指定了r.receiverTime为当前时间表示AMS将一个有序
// 广播发送给BroadcastRecord对象r所描述的广播转发任务的下一个目标广播接收者处理的时间。如果这个
// 广播接收者不能再timeoutTime之内完成这个有序广播,AMS就会任务它超时。
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
...
// 设置超时,传入参数是r.receiverTime + mTimeoutPeriod,也就是开始时间加上超时时间
// mTimeoutPeriod,mTimeoutPeriod初始化是在BroadcastQueue初始化的时候传入的,
// 也就是在AMS(AMS构造函数中)中初始化mFgBroadcastQueue和mBgBroadcastQueue时传入的
// BROADCAST_FG_TIMEOUT = 10 * 1000和BROADCAST_BG_TIMEOUT = 60 * 1000,
// 这里开始埋了ANR的雷
setBroadcastTimeoutLocked(timeoutTime);
}
// 上面分析的时候对于BroadcastRecord.receivers里面包含两种receiver接收者:order广播下的
// 动态注册接收者和静态接收者,这两种receiver处理的方式是不一样的,对于order广播下的动态注册
// receiver而言,接收者进程一定是已经启动的,但是对于静态接收者receiver而言,当前的receiver进程
// 可能还没有启动,因此动态和静态的receiver处理的逻辑不一样,需要分开处理,而静态接收者又分为进程
// 已经启动和尚未启动两种情况。
final BroadcastOptions brOptions = r.options;
// 得到下一个广播接收者
final Object nextReceiver = r.receivers.get(recIdx);
// 如果当前nextReceiver是一个BroadcastFilter类型,说名是一个动态注册接收者,不需要启动一个进程,
// 直接调用deliverToRegisteredReceiverLocked函数发送广播
if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter) nextReceiver;
...
// 上面已经分析
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
// 检查BroadcastRecord对象r所描述的广播转发任务是否用来转发无序广播的。
if (r.receiver == null || !r.ordered) {// 如果是
...
// 设置IDLE状态,表示AMS不需要等待它的前一个目标广播接收者处理完成一个广播就可以将该广播
// 继续发送给它的下一个目标广播接收者处理
r.state = BroadcastRecord.IDLE;
// 调用下面函数就是为了将一个广播继续发送给BroadcastRecord对象r所描述的广播转发任务的
// 下一个目标广播接收者处理的
scheduleBroadcastsLocked();
} else {
...
}
return;
}
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
// 如果不是动态的说明是一个静态注册接收者(如果动态的上面if中处理并进行拦截),此时进程可能没有启动
ResolveInfo info =
(ResolveInfo) nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
// 是否跳过该广播接收者不处理
boolean skip = false;
...
// 得到ResolveInfo对象info所描述的广播接收者的android:process属性值,即它需要运行在的应用程序
// 进程的名称,并且保存在变量targetProcess中
String targetProcess = info.activityInfo.processName;
// 获取当前广播接收者的进程记录,也就是该静态广播接收者是否已经运行
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
...
// 跳过,恢复初始状态,开始下一个广播接收者的处理
if (skip) {
...
return;
}
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
...
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(receiverUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
...
}
// Is this receiver's application already running?
// 如果当前进程已经运行,则直接发给该进程,然后返回
if (app != null && app.thread != null) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
// 将广播发送给该进程处理
processCurBroadcastLocked(r, app);
// order广播是一种同步处理方式,因此处理完可以直接return
return;
} catch (RemoteException e) {
...
} catch (RuntimeException e) {
...
return;
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
...
// 静态接收者进程尚未启动,调用AMS的startProcessLocked函数启动该接收者进程,并将当前正在等待进程
// 启动的BroadcastRecord存储到mPendingBroadcast里面,这个就是静态广播拉起应用的原理,如果应用
// 没有启动,注册一个静态广播(一般厂商会修改只能启动自己的应用)。到这里又开始进入等待进程启动的
// 过程,进程启动完成后才能处理广播
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;
}
// 正在启动接收者进程,将正在启动的BroadcastRecord记录存储到mPendingBroadcast中,同时将当前正在
// 启动的接收者进程在所有接收者中的索引存储到mPendingBroadcastRecvIndex,如果当前广播接收者处理
// 完,需要继续从mPendingBroadcastRecvIndex计算到下一个接收者发送当前广播
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
复制代码
我们根据第五步的发送消息类型可以跟踪到,消息执行是在BroadcastHandler的handleMessage方法中调用的,这里出入一个参数fromMsg,也就是该广播是不是通过Handler发送消息的方式传递的,那么我们上面知道我们这就是通过这种方式执行的,所以在代码中会将mBroadcastsScheduled参数设置为false,也就是这个消息开始执行了则开始运行下一个消息发送。还有就是通过直接调用的方式来执行的,那么就不会改变这个参数。
接着看,先执行无序广播,无序广播接收者不需要等待,所以直接for循环执行,调用deliverToRegisteredReceiverLocked方法发送广播。
7、deliverToRegisteredReceiverLocked
// AMS将一个广播发给一个目标广播接收者之前,有可能需要检查这个广播的发送者和接收者的权限。这个权限检查是
// 双向的,即需要检查一个广播发送者是否有权限向一个目标广播接收者发送广播,以及一个目标广播接收者是否有权限
// 接收一个广播发送者发过来的一个广播。这两个权限主要是调用AMS的成员函数checkComponentPermission来检查
// 对方的PID以及UID是否符合要求
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
// 标记是否要跳过该广播接收者
boolean skip = false;
// 需要检查广播发送者的权限
...
// r.requiredPermissions != null为true表示需要检查广播接收者的权限
...
// 如果要跳过,则设置该广播结束
if (skip) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
}
...
// order广播,所有的接收者需要依次以一种同步的方式发送广播,
// 可以看到order广播在BroadcastRecord保存了几个状态
if (ordered) {
// IBinder类型,代表当前的接收者
r.receiver = filter.receiverList.receiver.asBinder();
// 当前正在处理的BroadcastFilter,和上面的receiver是对应好的
r.curFilter = filter;
filter.receiverList.curBroadcast = r;
r.state = BroadcastRecord.CALL_IN_RECEIVE;
if (filter.receiverList.app != null) {
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceiver = r;
mService.updateOomAdjLocked(r.curApp);
}
}
try {
if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
// 如果正在备份或者恢复备份跳过,
// 如果是一个有序广播,则执行下一个广播
if (ordered) {
skipReceiverLocked(r);
}
} else {
// 如果不需要进行权限检查或者通过权限检查,调用performReceiveLocked发送广播
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
}
...
} catch (RemoteException e) {
...
}
}
复制代码
我们这里不是有序广播,正常也不会备份,所以会走else里面的方法performReceiveLocked。如果是有序广播我们后面再分析
8、performReceiveLocked
/**
* 由前面可知,当一个Activity或者一个Service将一个广播接收者注册到AMS中时,它所在的应用程序进程会先将
* 这个广播接收者封装成一个类型为InnerReceiver的Binder本地对象,然后再注册到AMS中。因此当AMS要将一个
* 广播发给一个目标广播接收者处理时,实际上是将这个广播转发给封装了这个目标广播接收者的一个InnerReceiver
* 对象来处理
*
* AMS向一个应用程序进程发送一个广播时,采用的是异步进程间通信方式。前面提到,发送给一个Binder实体对象
* 的所有异步事物都是保存在一个异步事物队列中的。由于保存在一个异步事物队列中的异步事物在同一时刻只有一
* 个会得到处理,即只有队列头部的异步事物才会得到处理,因此AMS就可以保证它发送给同一个应用程序的所有广
* 播都是按照这个发送顺序来串行的被接受和处理
*/
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
// app不为空,表示进程已经启动,调用ActivityThread.scheduleRegisteredReceiver发送当前广播
if (app != null) {
if (app.thread != null) {// 因为是动态注册广播,所以一般不为空
try {
// 这里scheduleRegisteredReceiver函数是一个Binder调用,注释上面说的很清楚,
// 要用one-way calls像动态的注册进程发起Binder调用,意思就是在Binder调用里面会
// 加上IBinder.FLAG_ONEWAY标记,Binder客户端(动态注册进程)只要一收到Binder调用的
// 命令和数据,立马返回到Binder服务端(AMS进程),是一个异步的调用方式。
// 大致流程是:
// 1) ApplicationThreadNative.ApplicationThreadProxy. scheduleRegisteredReceiver(system_server)
// 2) Binder驱动 (Binder驱动进程,ONEWAY)
// 3)ApplicationThreadNative. scheduleRegisteredReceiver(应用进程,Binder线程向主线程发送消息)
// 4)Binder驱动返回 (Binder驱动进程)
// 5)ActivityThread. scheduleRegisteredReceiver(应用进程,处理消息)
// 调用ApplicationThread对象的Binder代理对象的函数来向它发送广播
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
...
} catch (RemoteException ex) {
...
}
} else {
...
}
} else {
// 直接调用与它关联的一个InnerReceiver对象的Binder代理对象的成员函数performReceive来向它发送广播
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
复制代码
如果进程存在,则执行ActivityThread.scheduleRegisteredReceiver方法,否则直接调用receiver.performReceive方法发送广播。因为我们动态注册的所以会执行第一种情况。
9、ActivityThread.scheduleRegisteredReceiver
// 处理非串行化动态广播,非串化ordered是false,这里的receiver对应的是
// LoadedApk.ReceiverDispatcher.InnerReceiver对象
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
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);
// 调用LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive函数
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
复制代码
这里我们看到和上面一样最终还是执行receiver.performReceive方法来发送广播。
10、LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
...
rd = mDispatcher.get();
...
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
...
try {
...
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
} catch (RemoteException e) {
...
}
}
}
}
复制代码
我们上一章注册广播是讲过LoadedApk.ReceiverDispatcher对象封装了广播接收者,如果该广播接收者注册了,那么该对象就会存在,则会调用LodedApk.ReceiverDispatcher.performReceive,否则调用AMS.finishReceiver方法,我们先看有广播的情况。
11、LodedApk.ReceiverDispatcher.performReceive
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
// 首先将参数Intent所描述的一个广播封装成一个Args对象,然后将这个Args对象封装成一个消息对象,
// 然后将这个消息对象发送到应用程序主线程的消息队列中。
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
// 将当前广播信息放到主线程的Handler中进行处理,作为一个Runnable调度而不是在handleMessage中处理,
// 而是在Handler内部机制中,处理的时候会对应run函数,因此这里不久后会调用Args.run函数
..
// 上面将广播的参数封装在一个Args对象里面,然后通过post到主线程的消息队列里面
if (intent == null || !mActivityThread.post(args)) {
if (mRegistered && ordered) {
...
args.sendFinished(mgr);
}
}
}
复制代码
上面初始化了一个Args对象,该对象实现了Runnable接口,在if语句中调用post方法,会调用Args中的run方法。
13、Args.run
public void run() {
// mReceiver指向一个广播接收者
final BroadcastReceiver receiver = mReceiver;
...
// 这里处理的是动态广播接收者,默认认为接收者BroadcastReceiver已经存在
try {
...
// 接受广播
receiver.onReceive(mContext, intent);
// 然后调用BroadcastReceiver.PendingResult.finish函数,也就是下面的finish函数
} catch (Exception e) {
// 检查当前广播是否是有序广播,并且广播接收者是否已经注册到AMS中
if (mRegistered && ordered) {
// 通知AMS,它前面转发过来的有序广播已经处理完了,这时AMS就可以继续将这个有序广播
// 转发给下一个目标广播接收者了
sendFinished(mgr);
}
...
}
...
}
复制代码
在这里调用BroadcastReceiver.onReceive方法,这样就会执行完一次无需广播发送过程。我们再回到第10步,如果对象接收者不存在则调用AMS.finishReceiver
15、AMS.finishReceiver
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort, int flags) {
...
try {
boolean doNext = false;
BroadcastRecord r;
// 首先辨别出当前receiver所在的BroadcastRecord属于前台广播还是后台广播,然后在对应的
// BroadcastQueue中找出对应的BroadcastRecord,里面的finishReceiverLocked函数在前面介绍过,
// 主要是重新设置BroadcastRecord里面一些状态变量,以便于BroadcastRecord将广播发送给下一个
// 接收者。尤其的,如果前面的mAbortBroadcast设置为true,那么BroadcastRecord的成员resultAbort
// 会设置成true
synchronized (this) {
BroadcastQueue 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);
}
}
// 立马调度一次发送广播,发送下一次广播,但是processNextBroadcast是一个同步函数,
// 一次只能处理一个请求
if (doNext) {
r.queue.processNextBroadcast(false);
}
...
} finally {
...
}
}
复制代码
这里是如果发送广播时接收者不存在,那么要完成该次广播,并且判断是否执行发送给下一个广播接收者,如果需要发送给下个广播接收者要再次调用BroadcastQueue.processNextBroadcast方法。这样就又回到了前面第六步。我们再往前看BroadcastQueue.processNextBroadcast方法,执行完无序广播后开始执行有序广播,因为有序广播是一个执行完再执行下一个所以必须设置超时,并且如果超时要立即接受广播,
17、broadcastTimeoutLocked
/**
* AMS在处理order模式广播接收者时,会为每一个order模式广播处理设置超时时间,并且超时时间是各个接收者之间
* 相互独立,前面分析超时通过setBroadcastTimeoutLocked函数建立超时时间点消息的,本以为每次处理完以后,
* 调用cancelBroadcastTimeoutLocked函数取消当前接收者的超时消息,但是实际上用了一种更加高效的方法处理了
* 超时机制,在每个order模式receiver开始处理的时候设置超时消息,BroadcastRecord.receiverTime记录了
* 当前receiver开始处理的时间点
*
* BroadcastRecord超时:注意这里的超时和我们常说的广播超时ANR不是一个概念,这个BroadcastRecord超时是针
* 对当前BroadcastRecord. receivers里面剩余的所有的成员而言的,比如说当前receivers里面剩余4个广播接收者,
* 那么这个超时的时间: 2*(4*mTimeoutPeriod) 至于这个mTimeoutPeriod,对于前台广播mFgBroadcastQueue
* 和mBgBroadcastQueue后台广播时间:BROADCAST_FG_TIMEOUT = 10 * 1000
* 和BROADCAST_BG_TIMEOUT = 60 * 1000(AMS中),也就是对于有4个成员的receivers 后台广播的
* BroadcastRecord而言超时的时间为:2*(4*10*1000)=80 000ms=80s,当出现这种超时的时候,当前正在处理的
* 广播接收者会出现ANR,并且导致后面尚未接收到广播的收不到当前的广播。broadcastTimeoutLocked函数会将
* mOrderedBroadcasts中下标为0的应用进程ANR,下面forceReceive设置为true,走到下面的if判断里面会将当前
* 正在处理的BroadcastRecord从mOrderedBroadcasts中remove掉,导致receivers后面的成员没有收到广播,
* 并且将r设置为null,接着就处理broadcastTimeoutLocked里面的下一个广播记录BroadcastRecord。例如,
* receivers里面包含4个成员,但是第1个接收者在80s内都没有处理完,那么这个接收者进程会收到ANR,
* 并且后面的3个广播接收者都收不到当前的广播。
*
* 注释来自于:http://blog.csdn.net/houliang120/article/details/51607170
*
* @param fromMsg
*/
final void broadcastTimeoutLocked(boolean fromMsg) {
...
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
if (mService.mDidDexOpt) {
...
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
...
long timeoutTime = r.receiverTime + mTimeoutPeriod;
// 举个例子:
// 假设receiverA在100s的时候开始处理,超时时间为10s,那么receiverA的超时时间点就是110s,
// 但是receiverA在105s的时候已经处理完了,于是在105s的时候开始receiverB,但是并没有取消
// receiverA的超时消息,也就是在110s的时候仍然会走到这里的broadcastTimeoutLocked函数,
// receiverB开始处理,这时候r.receiverTime就是105s,对于receiverB而言超时时间应该是115s,
// 假设receiverB需要在112s才能处理完,在110s的时候broadcastTimeoutLocked函数处理的时候
// timeoutTime=115s,now=110s,这时候不会进行实际的超时处理,因为还没有到真实的超时时间,
// 所以重新设置超时时间点在115s。就这样根据当前BroadcastRecord.receiverTime的时间反复调整。
if (timeoutTime > now) {// 判断超时时间点和现在时间的关系,此处还没有超时
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
// 因为没有在每个广播处理完之后取消或者重置超时时间,从而导致提前检测到超时消息。取而代之,
// 设置一个初始超时时间点,然后每次出现超时事件的时候根据需要进行处理或者调整超时机制。
...
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.state == BroadcastRecord.WAITING_SERVICES) {
...
processNextBroadcast(false);
return;
}
...
}
复制代码
这里主要是超时的处理,因为注释已经非常详细,所以不再解释,所以我们在写广播接收到消息是最后采用Handler或者启动服务将消息处理的过程放到Handler中或者服务中,而不是直接在广播接收者中完成。
18、setBroadcastTimeoutLocked
// 设置超时时间
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (!mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
复制代码
如果广播还没有超时就执行完了就从新设置下一个广播的起始时间,方便计算下一个广播的超时。这个消息处理就不分析了,自己看一下。再回到第六步BroadcastQueue.processNextBroadcast方法中,如果广播发送完成,或者被中断或者取消,则判断是否发送最后的广播,如果要发送,则执行performReceiveLocked方法,这个方法在第八步讲了,这里就不再重复分析。我们知道广播有静态广播接收者和动态广播接收者,动态广播接收者进程都是启动的,但是静态的就不一定了,可能进程还没有启动,就想第六步中代码注释分析的一样,我们上面处理的都是动态注册的,不需要判断进程是否已经启动,但是静态广播就需要判断该进程是否已经启动了。这里的ResolveInfo表示静态广播接收者对象。找到对象后判断是否要跳过该广播,如果跳过则通过执行scheduleBroadcastsLocked方法发送消息执行下一个广播。如果不需要跳过该广播时,判断该进程是否存在,如果该静态广播的进程已经存在了则执行processCurBroadcastLocked,将广播发送给该进程处理,如果不存在则启动进程,并且将广播对象BroadcastRecord放入等待广播列表中,如果启动失败则接受该广播并判断是否执行下一个广播,我们先将一下进程存在时广播的处理,最后再分析进程启动时的广播处理。
21、processCurBroadcastLocked
// 这里只是将广播发送到接收者进程,需要一直等待接收者进程处理完广播后返回,AMS才能处理当前BroadcastRecord
// 里面的下一个receiver,所以直接返回就行了,反正需要等待的。
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
...
// 将进程的相关信息写入当前BroadcastRecord中相关的接收者
...
boolean started = false;
try {
...
// 处理广播,等待接收进程的返回
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);
...
} finally {
...
}
}
复制代码
调用scheduleReceiver方法处理广播。
22、ApplicationThread.scheduleReceiver
// 处理应用进程中接收到的静态广播消息,实际处理该广播的是ActivityThread.handleReceiver函数
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);
..
sendMessage(H.RECEIVER, r);
}
复制代码
通过Handler消息处理机制调用handleReceiver方法处理该静态广播
23、ApplicationThread.handleReceiver
// 主要包括三步:
// 1) 创建BroadcastReceiver对象
// 2) 执行onReceive函数
// 3) 向AMS发送处理结束消息
private void handleReceiver(ReceiverData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
// 1) 创建BroadcastReceiver对象
// 这里处理的是静态广播接收者,默认认为接收者BroadcastReceiver对象不存在
// 每次接受都会创建一个新的BroadcastReceiver对象
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManagerNative.getDefault();
BroadcastReceiver receiver;
try {
// 首先从AMS传递的intent中获取当前处理该广播的组件名称,然后通过反射创建一个BroadcastReceiver
// 对象,从这里可以看出来,静态广播处理的时候,每次都会创建一个新的BroadcastReceiver对象;
...
receiver = (BroadcastReceiver) cl.loadClass(component).newInstance();
} catch (Exception e) {
...
}
// 2) 执行onReceive函数
try {
// 创建Application对象,如果进程已经启动,Application对象已经创建
Application app = packageInfo.makeApplication(false, mInstrumentation);
...
// 调用接收者的onReceive方法,这里还调用了setPendingResult方法,详细内容请看
// BroadcastReceiver.goAsync方法。
receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
...
} finally {
sCurrentBroadcastIntent.set(null);
}
// 3) 向AMS发送处理结束消息
if (receiver.getPendingResult() != null) {
data.finish();
}
}
复制代码
上面最开始注释写了这里分为三步,创建BroadcastReceiver对象,执行onReceive函数,结束。这样进程存在时静态广播就发送完成了。最后我们分析需要启动进程时发送广播的流程,我们先看一张时序图。
在Android系统源码分析--Process启动过程一章我们分析了进程启动过程,在最后会调用ActivityThread.mian方法,我们从这个方法开始看:
0、ActivityThread.mian
/**
* 启动新的进程时调用Process的start方法会最终调用改函数
* 启动新的进程主要做了两件事:
* 1.在进程中创建了一个ActivityThread对象,并调用了它的成员函数attach向AMS发送一个启动完成的通知
* 2.调用Looper类的静态成员函数prepareMainLooper创建一个消息循环,并且在向AMS发送启动完成通知后,
* 使得当前进程进入到这个消息循环中
*
* @param args
*/
public static void main(String[] args) {
...
ActivityThread thread = new ActivityThread();
thread.attach(false);
...
}
复制代码
这里会初始化一个ActivityThread对象,然后调用该对象的attach方法,传入参数false。
1、ActivityThread.attach
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
...
// 获取AMS的代理对象,下面会调用它的成员函数attachApplication向AMS发送一个进程间通信请求,并且
// 将前面所创建的ApplicationThread(mAppThread)对象传递给AMS
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
// 这里向ActivityManagerService注册Client端的binder对象,它是一个binder线程
// ActivityManagerService中有关Activity声明周期的消息都会发送到ActivityThread中的
// 主线程mH处理
// mAppThread是一个Binder本地对象,AMS是通过它来和应用进程通讯的。(AMS的代理对象类型为
// ActivityManagerProxy,因此,接下来会调用ActivityManagerProxy类的成员函数向AMS发送一个
// 进程间通信请求)
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
} else {// system thread
...
}
...
}
复制代码
我们上面接受了传入参数是false,则会走if中的代码,也就是会执行mgr.attachApplication方法,通过注释我们知道会调用ActivityManagerProxy.attachApplication方法,然后传递到ActivityManagerService.attachApplication方法。
2、ActivityManagerProxy.attachApplication
public void attachApplication(IApplicationThread app) throws RemoteException {
...
// 通过mRemote(Binder)向AMS发送一个类型为ATTACH_APPLICATION_TRANSACTION的进程间通信请求
mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
...
}
复制代码
3、ActivityManagerService.attachApplication
// 用来处理ActivityMangerProxy传递过来的类型为ATTACH_APPLICATION_TRANSACTION的进程间通信请求
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
...
attachApplicationLocked(thread, callingPid);
...
}
}
复制代码
这里调用attachApplicationLocked方法:
4、attachApplicationLocked
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app);
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
...
}
复制代码
因为我们这里分析发送广播,所以我们只关心广播的相关处理,所以上面只保留了广播的内容,if语句中判断当前应用没有问题,并且有等待的广播,才会调用sendPendingBroadcastsLocked方法,前面我们知道静态广播有一种是进程不存在的,所以这个广播就要放到等待广播中,这里就开始处理等待广播的情况。
5、sendPendingBroadcastsLocked
// The app just attached; send any pending broadcasts that it should receive
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
for (BroadcastQueue queue : mBroadcastQueues) {
didSomething |= queue.sendPendingBroadcastsLocked(app);
}
return didSomething;
}
复制代码
mBroadcastQueues是包含前台优先级和后台优先级的广播队列,这里分别调用前台和后台优先级广播的BroadcastQueue.sendPendingBroadcastsLocked方法。
6、BroadcastQueue.sendPendingBroadcastsLocked
// 未启动进程的广播接收者需要先启动进程,最后到达这个函数
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
// 前面分析mPendingBroadcast用于存储当前正在等待进程启动的BroadcastRecord
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.pid == app.pid) {
...
try {
// 启动完成设置为null
mPendingBroadcast = null;
processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
...
}
}
return didSomething;
}
复制代码
这里是找到等待处理的广播并且判断是否为空,并且是否和当前进程的pid相同,也就是是不是找个进程的等待广播,如果是就调用processCurBroadcastLocked方法进行处理,这个方法在上面第21步中已经讲过,所以又回到了进程存在的情况下广播的处理。这样整个广播的处理就分析完了,代码量很大,但是逻辑很清楚,只需要对着注释多看看看就明白了。下一篇我们开始讲Activity启动的源码分析。
为了方便记忆,这里添加一个发送广播的流程图,这样对照着上面的代码流程再看就会好理解很多。
注
Android开发群:192508518
微信公众账号:Code-MX
首发地址:www.codemx.cn
注:本文原创,转载请注明出处,多谢。