[toc]
本文基于 Android 7.1.1,分析发送广播的流程,转载请说明出处!
写本文的目的:
- 了解广播发送的流程;
- 了解 AMS 对广播接收者组件的管理;
- 记录自己的研究和分析;
0 综述
在 Android 系统中,有如下种类的广播,他们的发送方式各不一样,我们先来简单的了解一下:
1. 普通广播
发送普通广播的方法如下:1
2
3sendBroadcast(...)
sendBroadcastMultiplePermissions(...)
sendBroadcastAsUser(...)
普通广播的发送参数 serialized 为 false
,sticky
也为 false
,表示普通广播既不是粘性的,也不是无序的;
对于普通广播,AMS
会针对其接收者的类型做不同的处理:
- 对于动态注册的广播接收者,
AMS
会遍历对应的目标接收者集合,依次发送广播; - 对于静态注册的广播接收者,
AMS
在发送普通广播时,会按照有序的方式来进行分发;
由此可见,对于普通广播来说,动态注册的广播接收者会先接收到广播!
2. 有序广播
发送有序广播的方法如下:1
2sendOrderedBroadcast(...)
sendOrderedBroadcastAsUser(...)
有序广播的发送参数 serialized
为 true
,sticky
为 false
;
对于有序广播,AMS 会收集能够匹配的静态注册和动态注册的广播接收者,按照优先级们,依次有序的发送广播!
AMS 收到上一个 BroadcastReceiver
处理完毕的消息返回后,才会将广播发送给下一个 BroadcastReceiver
;其中,任意一个 BroadcastReceiver
,都可以中止后续的广播分发流程;同时,上一个 BroadcastReceiver
可以将额外的信息添加到广播中。
对于发送给静态注册的广播接收者的普通广播,AMS
是将其按照发送有序广播的方式来进行发送的,因为静态注册的接收者由于其注册方式的特殊性,其所在进程可能没有被启动,所以如果采用并发的方式发送,那么系统需要在同一时间启动大量的进程,这显然不是合理的设计!
3. 粘性广播
发送粘性广播的方法如下:1
2sendStickyBroadcast(...)
sendStickyBroadcastAsUser(...)
粘性广播的发送参数 serialized
为 false
,sticky
为 true
;
粘性广播的特性是系统会保存这个广播,当系统中又新注册了一个广播接收者时,该接收者会立刻接收到这个广播,粘性广播默认属于普通广播!
4. 粘性有序广播
发送粘性有序广播的方法如下:1
sendStickyOrderedBroadcast(...)
粘性广播的发送参数 serialized
为 true
,sticky
为 true
;
粘性有序广播本质上属于有序广播,只不过其具有 “粘性”!
本篇文章,会主要分析下广播的发送和处理过程:
1 发送者进程
1.1 ContextWapper.sendBroadcast
1 |
|
这里的 mBase
对象是 ContextImpl
实例,其他的方法的调用方式也是一样的!
1.2 ContextImpl.sendBroadcast
进入 ContextImpl
,我们看看不同的 send
方法都做了什么操作:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
//【1.3】开始发送广播!
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();
}
}
可以看到,这里最终调用了 AMS
的 broadcastIntent
方法!
1 | /** {@hide} */ |
getUserId 方法用来获得进程所在的设备用户 id
,一般我们在应用程序进程中,只能获得当前的设备用户 id
,这个 id
是进程启动时获得的!
1.3 ActivityManagerProxy.broadcastIntent
我们来看一下,发送普通广播的参数传递:
IApplicationThread caller
:发送者进程的ApplicationThread
对象,用于跨进程通信!Intent intent
:广播Intent
String resolvedType
:IIntentReceiver resultTo
:广播发送后的结果反馈对象,这里传入null
!int resultCode
:广播发送后的结果反馈码,这里传入Activity.RESULT_OK
!String resultData
: 广播发送后的结果反馈数据,这里传入null
!Bundle map
:传入 null;String[] requiredPermissions
:广播接受者需要的权限,传入null
或者指定的权限数组!int appOp
:发送广播相关的应用操作,传入AppOpsManager.OP_NONE
或者指定的操作!Bundle options
:广播携带的参数信息,传入null
或者指定的Bundle
对象!boolean serialized
:广播是否是序列化的,普通的广播传入false
;boolean sticky
: 广播是否是粘性的,传入false
;int userId
:当前的设备用户id
;
1 | public int broadcastIntent(IApplicationThread caller, |
下面,就是进入系统进程!
2 系统进程
首先,进入 ActivityManagerNative
中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case BROADCAST_INTENT_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
//【1】获得代理对象 ApplicationThreadProxy!
IApplicationThread app =
b != null ? ApplicationThreadNative.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
//【2】显然这里的 resultTo = null;
b = data.readStrongBinder();
IIntentReceiver resultTo =
b != null ? IIntentReceiver.Stub.asInterface(b) : null;
//【3】得到 Activity.RESULT_OK
int resultCode = data.readInt();
//【4】均为 null!
String resultData = data.readString();
Bundle resultExtras = data.readBundle();
String[] perms = data.readStringArray();
int appOp = data.readInt();
Bundle options = data.readBundle();
boolean serialized = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
int userId = data.readInt();
//【2.1】调用 AMS 的 broadcastIntent 方法继续发送广播!
int res = broadcastIntent(app, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, perms, appOp,
options, serialized, sticky, userId);
reply.writeNoException();
reply.writeInt(res);
return true;
}
}
我们进入 AMS
的方法中去看看:
2.1 AMService.broadcastIntent
这里的参数和之前保持一致,不用多说!
1 | public final int broadcastIntent(IApplicationThread caller, |
2.2 AMService.broadcastIntentLocked
这里我们再来看看参数传递:
ProcessRecord callerApp
:发送者所在的进程对象String callerPackage
:发送者所在的应用包名Intent intent
:广播Intent
String resolvedType
:IIntentReceiver resultTo
: 广播发送后的结果反馈对象;int resultCode
:广播发送后的结果反馈码,这里传入Activity.RESULT_OK
;String resultData
:广播发送后的结果反馈数据,这里传入null
;Bundle resultExtras
: 传入null
;String[] requiredPermissions
: 广播接受者需要的权限,传入null
或者指定的权限数组;int appOp
:发送广播相关的应用操作,传入AppOpsManager.OP_NONE
或者指定的操作;Bundle bOptions
:广播携带的参数信息,传入null
或者指定的Bundle
对象;boolean ordered
:广播是否是有序广播还是普通广播;boolean sticky
:广播是否是粘性的;int callingPid
:发送者所在进程的pid
;int callingUid
:发送者所在进程的uid
;int userId
:目标设备用户id
;
1 | final int broadcastIntentLocked(ProcessRecord callerApp, |
我们来总结一下,这个方法方法流程:
1. 给 intent 添加
Intent.FLAG_EXCLUDE_STOPPED_PACKAGES
标志位,禁止广播发送给被强制停止的应用!- 1.1 如果系统没有启动完成,就给广播添加
Intent.FLAG_RECEIVER_REGISTERED_ONLY
标志位,禁止启动静态广播接收者!
- 1.1 如果系统没有启动完成,就给广播添加
2. 针对一些特殊
action
的广播进行处理,比如uid
被移除,package
相关的广播等等!3. 如果是粘性广播,就需要将广播收集到
mStickyBroadcasts
集合中!- 3.1 权限校验:发送粘性广播必须配置
android.Manifest.permission.BROADCAST_STICKY
权限! - 3.2 对于粘性广播,不能强制广播接收者应具有的权限!
- 3.3 如果有相同的全局粘性广播,会产生冲突!
- 3.1 权限校验:发送粘性广播必须配置
4. 收集能够匹配当前广播的广播接收者:
- 4.1 尝试收集能够匹配当前广播的静态注册的广播接收者(如果
intent
设置了Intent.FLAG_RECEIVER_REGISTERED_ONLY
,不收集) - 4.2 收集能够匹配当前广播动态注册的广播接收者
- 4.2.1 如果广播没有设置组件信息,那就从
mReceiverResolver
对象中收集动态广播接收者!
- 4.2.1 如果广播没有设置组件信息,那就从
- 4.1 尝试收集能够匹配当前广播的静态注册的广播接收者(如果
5. 如果广播是普通广播,且存在匹配的动态注册的广播接收者
registeredReceivers
!- 5.1 创建对应的
BroadcastRecord(registeredReceivers)
对象,这里的BroadcastRecord.receivers
都是动态注册的广播接收者! - 5.2 并添加到指定的队列(前台后台由
Intent.FLAG_RECEIVER_FOREGROUND
决定)的无序并行集合mParallelBroadcasts
中! - 5.3 触发广播发送任务,清空
registeredReceivers
!
- 5.1 创建对应的
6. 将收集到的静态广播接收者
receivers
和动态广播接收者registeredReceivers
合并到同一个列表receivers
中,只有在收集到静态注册的广播接收者才会合并!- 6.1 这首先会针对一些特殊
action
做了处理,防止应用自启动! - 6.2 对于普通广播,到这里就只剩下静态广播接收者了,无需合并;
对于有序广播,到这里静态广播接收者和动态广播接收者都没有处理,那就按照优先级,将二者合并到静态广播接收者 receivers 中!
- 6.1 这首先会针对一些特殊
7. 处理
6
中获得的合并广播接收者集合!- 7.1 创建对应的
BroadcastRecord(receivers)
对象,注意这里的BroadcastRecord.receivers
可能只有静态广播接收者,可能静态动态都有! - 7.2 并添加到指定的队列(前台后台由
Intent.FLAG_RECEIVER_FOREGROUND
决定)的无序并行集合mParallelBroadcasts
/ 有序集合mOrderedBroadcasts
中! - 7.3 触发广播发送任务;
- 7.1 创建对应的
接下来,我们来看看这个方法中的一些细节:
2.2.1 AMService.collectReceiverComponents
collectReceiverComponents
方法用来收集指定设备用户下的静态注册的广播接收者:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users) {
int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
// 用来保存查询结果!
List<ResolveInfo> receivers = null;
try {
// 用来保存在所有设备用户下都只有一个实例的广播接收者!
HashSet<ComponentName> singleUserReceivers = null;
boolean scannedFirstReceivers = false;
for (int user : users) {
// Skip users that have Shell restrictions, with exception of always permitted
// Shell broadcasts
if (callingUid == Process.SHELL_UID
&& mUserController.hasUserRestriction(
UserManager.DISALLOW_DEBUGGING_FEATURES, user)
&& !isPermittedShellBroadcast(intent)) {
continue;
}
// 查询指定设备用户下的静态广播接收者!
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
// 如果不是系统用户,那就要检测该设备用户下的所有静态广播接收者,
// 过滤掉设置了 systemUserOnly 属性的接收者!
if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
newReceivers.remove(i);
i--;
}
}
}
if (newReceivers != null && newReceivers.size() == 0) {
newReceivers = null;
}
// 保存查询结果,查询第一个设备用户下的广播接收者进入该分支!
if (receivers == null) {
receivers = newReceivers;
} else if (newReceivers != null) {
// 后续设备用户下的查询会进入该分支!
if (!scannedFirstReceivers) {
// 收集那些在所有的设备用户下都只有一个实例的广播接收者!
scannedFirstReceivers = true;
for (int i=0; i<receivers.size(); i++) {
ResolveInfo ri = receivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
ComponentName cn = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
if (singleUserReceivers == null) {
singleUserReceivers = new HashSet<ComponentName>();
}
singleUserReceivers.add(cn);
}
}
}
for (int i=0; i<newReceivers.size(); i++) {
ResolveInfo ri = newReceivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
ComponentName cn = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
if (singleUserReceivers == null) {
singleUserReceivers = new HashSet<ComponentName>();
}
// 这里保证了在所有的设备用户下都只有一个实例的广播接收者只会被添加一次!
if (!singleUserReceivers.contains(cn)) {
singleUserReceivers.add(cn);
receivers.add(ri);
}
} else {
receivers.add(ri);
}
}
}
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
return receivers;
}
这里要注意:
如果我们在 AndroidManifest.xml
中给静态广播接收者设置了 android:singleUser="true"
属性的话,那么他对应的 ActivityInfo.flags
会被设置 ActivityInfo.FLAG_SINGLE_USER
位,这样该接收者在所有的设备用户下都只会有一个实例!
具体的关于静态广播接收者的查询过程,请去看另一篇文章!
2.2.2 PackageManagerS.isProtectedBroadcast
1 |
|
我们来看看如何判断一个广播是否是受保护的广播:
- 该广播在
mProtectedBroadcasts
列表中; - 该广播是指定的一些广播;
二者满足任何一个条件就行了!
其中 mProtectedBroadcasts 是在 PMS 里面对 protected-broadcast 类型的标签进行解析得到的,这里我们不细看!
2.3 广播的分发处理
接下来,我们去看看广播队列是如何处理其内部的广播分发的,我们先回到 ActivityManagerS 中去!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20... ... ...
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
"Enqueueing broadcast " + r.intent.getAction());
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
... ... ...
这里是分发广播的核心代码段,我们一个一个来分析!
2.3.1 ActivityManagerS.broadcastQueueForIntent
1 | BroadcastQueue broadcastQueueForIntent(Intent intent) { |
用于判断将这个 Intent
插入哪个队列,插入的依据是 Intent
是否设置了 Intent.FLAG_RECEIVER_FOREGROUND
标志位!
这里我们可以看到 ActivityManagerS
通过 2 个队列来管理内部的广播:
mFgBroadcastQueue
:前台广播队列;mBgBroadcastQueue
:后台广播队列;
2.3.2 new BroadcastRecord
1 | BroadcastRecord(BroadcastQueue _queue, |
这些属性其实很简单!
2.3.3 BroadcastQueue.replaceXXXXX
替换掉 BroadcastQueue 中 mParallelBroadcasts 已有的正在等待分发的 intent:
- replace ParallelBroadcast
1 | public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) { |
同样的,如下操作:
- replace Ordered Broadcast
1 | public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) { |
不多说了!
2.3.4 BroadcastQueue.enqueueOrderedBroadcastLocked
这里是将广播添加到广播队列的并行集合或者是有序集合中:
1 | public void enqueueParallelBroadcastLocked(BroadcastRecord r) { |
通过前面的分析,我们可以看出:
- 对于普通广播来说
- 先会被添加到
mParallelBroadcasts
中,BroadcastRecord.receivers
为动态注册的广播接收者集合,采用并发的发送方式; - 再被添加到
mOrderedBroadcasts
中,BroadcastRecord.receivers
为静态注册的广播接收者集合,采用有序的发送方式;
- 先会被添加到
- 对于有序广播来说:
- 会被直接添加到
mOrderedBroadcasts
中,BroadcastRecord.receivers
为静态注册和动态注册的广播接收者集合,按照优先级排序,采用有序的发送方式;
- 会被直接添加到
2.3.5 BroadcastQueue.scheduleBroadcastsLocked
接下来,就是触发广播的分发调度!1
2
3
4
5
6
7
8
9
10
11
12
13public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
//【×2.4】发送 `BROADCAST_INTENT_MSG` 消息!
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
下面就进入了 BroadcastHandler 中!
2.4 BroadcastHandler.handleMessage
1 | private final class BroadcastHandler extends Handler { |
收到 BROADCAST_INTENT_MSG
消息后,调用 processNextBroadcast
方法,执行广播分发!
2.5 BroadcastQueue.processNextBroadcast
processNextBroadcast 方法比较长,需要我们仔细分析,参数传递:
- boolean fromMsg:传入 true,表示触发是来自消息调用!
1 | final void processNextBroadcast(boolean fromMsg) { |
这个方法的逻辑很长,我们来总结一下他的业务流程:
- 1 立刻分发
mParallelBroadcasts
集合中的目标为动态接收者的普通广播,方式方式为并行!- 1.1 调用【
deliverToRegisteredReceiverLocked
】发送广播!
- 1.1 调用【
- 2 处理正在等待目标进程启动的广播
mPendingBroadcast
,mPendingBroadcast
是有序的发送方式!- 2.1 如果进程没有启动成功,就等待进程启动后处理改广播,
return
;
- 2.1 如果进程没有启动成功,就等待进程启动后处理改广播,
- 3 移除
mOrderedBroadcasts
列表中的无需发送的广播,并找到下一个需要发送的广播,mOrderedBroadcasts
集合中的所有广播都是有序发送!- 3.1 如果某个广播超时了,就强行结束广播,并设置广播的状态为
BroadcastRecord.IDLE
; - 3.2 如果某个广播的状态不是
BroadcastRecord.IDLE
,说明上一个接收者没有结束广播处理,return
; - 3.3 如果某个广播没有接收者,或者已经分发给了所有的目标接受者,或者被终止传递,或者广播超时了,那就移除该广播!
如果指定了r.resultTo
,那还需要将该广播发送给resultTo
接受者!
- 3.1 如果某个广播超时了,就强行结束广播,并设置广播的状态为
4 有序分发
mOrderedBroadcasts
的广播,根据下一个接收者的不同,做不同的处理!- 4.1 如果是动态接收者,调用【
deliverToRegisteredReceiverLocked
】发送广播,然后等待处理结果,return
! 4.2 如果是静态接收者,先要判断是否 skip 该接收者;
4.2.1 跳过的触发条件:
- sdk 不匹配,跳过; - 发送者不具有接收者要求的权限,跳过; - 接收者不具有发送者要求的权限,跳过; - 广播不满足 `AMS` 的 `IntentFirewall` 防火墙要求,跳过; - 接收者是单例,但是没有申明 `android.Manifest.permission.INTERACT_ACROSS_USERS`,跳过; - 接收者所在进程 `crash` 了,跳过; - 接收者所在 `package` 不可用,跳过; - 接收者所在进程不允许后台启动,跳过; </br>
4.2.2 如果需要跳过该接受者,执行
scheduleBroadcastsLocked
,进行下一次广播分发;- `r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED`; - `r.receiver = null`; - `r.curFilter = null`; - `r.state = BroadcastRecord.IDLE`; </br>
4.2.3 如果不跳过,就执行发送操作,这里需要判断静态接收者的进程是否启动;
- 4.2.3.1 静态广播接收者所在的进程已经启动,调用【`processCurBroadcastLocked`】 发送广播,并等待结果,`return`; - 4.2.3.2 静态广播接收者所在的进程没有启动,调用 `startProcessLocked` 启动进程: - 启动成功,将当前广播保存到 `mPendingBroadcast` 中,等待处理! - 启动失败,那就 执行 `scheduleBroadcastsLocked`,进行下一次广播分发;
- 4.1 如果是动态接收者,调用【
梳理后,整个方法的业务逻辑很清楚了,下面我们来关注一些细节:
2.5.1 发送给动态注册的接收者
2.5.1.1 BroadcastQueue.deliverToRegisteredReceiverLocked
对于目标是动态接收者的普通广播(ordered
为 false
),和目标是动态接收者的有序广播(ordered
为 true
),都是通过 deliverToRegisteredReceiverLocked
直接发送的!
下面,我们来看看如何给动态注册的接收者发送广播!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
//【1】判断是否跳过这个接收者,这个和前面的流程很类似!
boolean skip = false;
//【2】首先是权限校验,校验发送者是否有接收者定义的权限,没有,跳过该接收者!
// 权限如果是授予的,那还要再校验下 appOps!
if (filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
+ r.callingPid + ", uid=" + r.callingUid + ")"
+ " requires " + filter.requiredPermission
+ " due to registered receiver " + filter);
skip = true;
} else {
final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
if (opCode != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(opCode, r.callingUid,
r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
+ r.callingPid + ", uid=" + r.callingUid + ")"
+ " requires appop " + AppOpsManager.permissionToOp(
filter.requiredPermission)
+ " due to registered receiver " + filter);
skip = true;
}
}
}
//【3】校验接收者者是否有发送者定义的权限,没有,跳过该接收者!
// 权限如果是授予的,那还要再校验下 appOps!
if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
for (int i = 0; i < r.requiredPermissions.length; i++) {
String requiredPermission = r.requiredPermissions[i];
int perm = mService.checkComponentPermission(requiredPermission,
filter.receiverList.pid, filter.receiverList.uid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: receiving "
+ r.intent.toString()
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")"
+ " requires " + requiredPermission
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
break;
}
int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
&& mService.mAppOpsService.noteOperation(appOp,
filter.receiverList.uid, filter.packageName)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent.toString()
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")"
+ " requires appop " + AppOpsManager.permissionToOp(
requiredPermission)
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
break;
}
}
}
//【4】校验接收者权限,这里跟踪下代码,由于传入的权限为 null,方法里面只针对 uid 做了判断
// 看是不是 root 或者 system,是不是隔离进程 uid;
if (!skip && (r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
int perm = mService.checkComponentPermission(null,
filter.receiverList.pid, filter.receiverList.uid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: security check failed when receiving "
+ r.intent.toString()
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")"
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
}
}
//【5】校验 ops!
if (!skip && r.appOp != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(r.appOp,
filter.receiverList.uid, filter.packageName)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent.toString()
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")"
+ " requires appop " + AppOpsManager.opToName(r.appOp)
+ " due to sender " + r.callerPackage
+ " (uid " + r.callingUid + ")");
skip = true;
}
//【6】判断是否允许后台发送广播,不允许就跳过!
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid,
filter.packageName, -1, true);
if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent
+ " to " + filter.receiverList.app
+ " (pid=" + filter.receiverList.pid
+ ", uid=" + filter.receiverList.uid + ")");
skip = true;
}
}
//【7】广播不通过防火墙校验,跳过该接收者!
if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, filter.receiverList.uid)) {
skip = true;
}
//【8】接收者所在进程 crash 了,就跳过接收者!
if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ " to " + filter.receiverList + ": process crashing");
skip = true;
}
//【9】如果跳过该接收者,那就设置该接收者为 DELIVERY_SKIPPED;
if (skip) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
}
//【10】如果需要 review 权限,那就拉起 review!
if (Build.PERMISSIONS_REVIEW_REQUIRED) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
filter.owningUserId)) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
}
}
//【11】开始发送广播!
r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
//【12】如果是有序广播,下面的分支!!
if (ordered) {
//【12.1】设置广播的目标接收者 IIntentReceier.Proxy 对象,用于挂进程拉起 onReceive 方法!
r.receiver = filter.receiverList.receiver.asBinder();
//【12.2】设置一系列相互引用;
r.curFilter = filter;
filter.receiverList.curBroadcast = r;
//【12.2】设置广播状态;
r.state = BroadcastRecord.CALL_IN_RECEIVE;
//【12.3】如果接收者所在进程已经启动!
if (filter.receiverList.app != null) {
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceiver = r;
mService.updateOomAdjLocked(r.curApp);
}
}
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
//【13】如果接收者的进程正在进行数据的备份和还原操作,那就跳过当前接收者!
// 发送给下一个接收者,先 finishReceiverLocked,再 scheduleBroadcastsLocked!!
if (ordered) {
skipReceiverLocked(r);
}
} else {
//【×2.5.1.3】发送广播,one way 异步传输,无需等待返回!
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
}
//【14】如果是有序广播,将广播状态置为 CALL_DONE_RECEIVE 已经接受!
if (ordered) {
//【14.1】修改广播状态为 CALL_DONE_RECEIVE!
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
} catch (RemoteException e) {
Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
if (ordered) {
r.receiver = null;
r.curFilter = null;
filter.receiverList.curBroadcast = null;
if (filter.receiverList.app != null) {
filter.receiverList.app.curReceiver = null;
}
}
}
}
整个方法的逻辑很简单!
2.5.1.2 BroadcastQueue.addBroadcastToHistoryLocked
将分发的广播加入历史集合中:
1 | private final void addBroadcastToHistoryLocked(BroadcastRecord r) { |
2.5.1.3 BroadcastQueue.performReceiveLocked
1 | void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, |
这里会调用 ApplicationThreadProxy
对象的 scheduleRegisteredReceiver
!
这里就要进入接收者所在的进程了!
2.5.1.4 ApplicationThreadP.scheduleRegisteredReceiver
1 | public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, |
接下来就会进入接收者所在进程!
2.5.2 发送给静态注册的接收者
对于静态注册的广播接收者(有序广播/普通广播),因为他的进程未必已经被启动,所以要分情况!
2.5.2.1 进程已启动
如果接收者所在的进程被启动了:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 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);
//【×2.5.2.1.1】那就调用 processCurBroadcastLocked 处理广播发送!
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
} catch (RuntimeException e) {
}
}
我们继续看:
2.5.2.1.1 BroadcastQueue.processCurBroadcastLocked
发送当前的广播!
1 | private final void processCurBroadcastLocked(BroadcastRecord r, |
这里就会进入接收者所在的进程,我们后面再看!
2.5.2.1.2 ApplicationThreadP.processCurBroadcastLocked
发送当前的广播!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
info.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
data.writeInt(resultCode);
data.writeString(resultData);
data.writeBundle(map);
data.writeInt(sync ? 1 : 0);
data.writeInt(sendingUser);
data.writeInt(processState);
//【1】发送 SCHEDULE_RECEIVER_TRANSACTION 消息,one way!!
mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
下面就进入了应用进程:
2.5.2.2 进程未启动
对于进程没有启动这种情况,我们需要主动的拉起接收者所在的进程:
1 | if ((r.curApp=mService.startProcessLocked(targetProcess, |
对于进程的启动,我们这里不详细的介绍,请看其他的博文,这里我就直接上结果:
2.5.2.2.1 ActivityManagerService.attachApplicationLocked
1 | private final boolean attachApplicationLocked(IApplicationThread thread, |
我们继续来看:
2.5.2.2.1.1 ActivityManagerS.isPendingBroadcastProcessLocked
进程启动后,会判断该进程中是否有有需要分发的广播:1
2
3
4
5boolean isPendingBroadcastProcessLocked(int pid) {
//【×2.5.2.2.1.2】判读是否有等待该进程启动的广播
return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
|| mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid);
}
其实判断的依据很简单:1
2
3public boolean isPendingBroadcastProcessLocked(int pid) {
return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
}
如果 BroadcastQueue
内部的 mPendingBroadcast
不为 null
,且 mPendingBroadcast.curApp.pid
等于当前进程的 pid
,就说明有目标为该进程的广播!
2.5.2.2.1.2 ActivityManagerS.sendPendingBroadcastsLocked
1 | boolean sendPendingBroadcastsLocked(ProcessRecord app) { |
下面我们去 BroadcastQueue
中去看看!
2.5.2.2.1.3 BroadcastQueue.sendPendingBroadcastsLocked
1 | public boolean sendPendingBroadcastsLocked(ProcessRecord app) { |
这里的 processCurBroadcastLocked
又回到了进程已启动的发送流程了!!
3 接收者进程
这里我们按照广播接收者的不同来分别看看:
3.1 动态接收者进程
发送给动态注册的接收者有 2
种广播,首先会进入 ApplicationThreadN
中:
1 | case SCHEDULE_REGISTERED_RECEIVER_TRANSACTION: { |
3.1.1 ApplicationThread.scheduleRegisteredReceiver
1 | public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, |
3.1.2 InnerReceiver.performReceive
继续看:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
if (intent == null) {
Log.wtf(TAG, "Null intent received");
rd = null;
} else {
//【1】如果广播不为 null,就获得对应的 ReceiverDispatcher 对象,用于分发广播!
rd = mDispatcher.get();
}
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction()
+ " seq=" + seq + " to " + (rd != null ? rd.mReceiver : null));
}
if (rd != null) {
//【×3.1.3】调用 ReceiverDispatcher 的 performReceive 方法,分发广播!
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
//【3】如果 rd 为 null,那就调用 AMS 的方法,立刻结束广播的发送!
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to unregistered receiver");
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
if (extras != null) {
extras.setAllowFds(false);
}
//【4】调用了 ams 的 finish receiver 方法,这个方法我们在取消注册的时候会看!
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
3.1.3 ReceiverDispatcher.performReceive
接着,进入 ReceiverDispatcher
,我们都知道 ReceiverDispatcher
保存了 BroadcastReceiver
和 InnerReceiver
的映射关系:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
//【*3.1.3.1】创建了一个 Args 对象,用于封装广播参数信息!Args 继承了 Runnable 对象!
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (intent == null) {
Log.wtf(TAG, "Null intent received");
} else {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
+ " seq=" + seq + " to " + mReceiver);
}
}
//【×3.1.4】调用 mActivityThread.post 执行 Args 任务!post 如果返回 false,说明任务执行失败!
if (intent == null || !mActivityThread.post(args)) {
//【1】任务执行失败,如果该广播是通过有序的方式发送的,还要通知系统,广播已经分发完成!
// 然后系统就会进行下一个广播的有序分发!
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
//【×3.3.2】调用了 args 父类 PendingResult 的 sendFinished 方法!
args.sendFinished(mgr);
}
}
}
这里我们回顾一下,采用有序的分发方式的广播有 2
种类型:
- 目标是静态注册的接收者的普通广播;
- 另外一种是目标是静态或者动态注册的接收者的有序广播;
这里的 ordered
如果是普通广播,那么为 false
,如果是有序广播,那么为 true
!
3.1.3.1 new Args
创建了一个 Args 对象,其继承了 PendingResult:
1 | final class Args extends BroadcastReceiver.PendingResult implements Runnable { |
这里的 mRegistered
是应用在动态注册接收者时,创建 ReceiverDispatcher
时设置为 true
的,这里显然为 true
!
3.1.4 Args.run
mActivityThread.post
方法,会在主线程执行任务 Args
,下面我们来看看 Args.run
方法:
1 | public void run() { |
mForgotten
属性是在 unregisterReceiver
的时候被置为 true
的,这里终于进入了 BroadcastReceiver
的 onReceive
方法!
3.2 静态接收者进程
发送给静态注册的接收者也有 2
种广播,首先会进入 ApplicationThreadN
中:
1 | case SCHEDULE_RECEIVER_TRANSACTION: |
3.2.1 ApplicationThread.scheduleReceiver
1 | public final void scheduleReceiver(Intent intent, ActivityInfo info, |
这里创建了一个 ReceiverData
对象,用来封装接收者的数据信息!
3.2.1.1 new ReceiverData
1 | static final class ReceiverData extends BroadcastReceiver.PendingResult { |
ReceiverData
继承了 BroadcastReceiver.PendingResult
对象,用来封装静态接收者的数据信息!
这里就和 Args 类似了!
3.2.2 H.handleMessage
我们去看看主线程 Handler H 是如何处理 H.RECEIVER 消息的:
1 | private class H extends Handler { |
3.2.3 ActivityThread.handleReceiver
1 | private void handleReceiver(ReceiverData data) { |
下面,我们进入到 ReceiverData
中去!!
3.3 PendingResult
3.3.1 PendingResult.finish
接着,调用 PendingResult .finish
结束广播的分发,通知系统继续广播的分发!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36public final void finish() {
// 如果 mType 为 TYPE_COMPONENT,静态接收者,进入 IF 分支!
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
// 若主线程的 QueuedWork 中有事情还未处理完,则必须让事情做完后,才通知 AMS 结果!
if (QueuedWork.hasPendingWork()) {
// 创建一个任务,通过 singleThreadExecutor 单线程依次执行!
QueuedWork.singleThreadExecutor().execute( new Runnable() {
public void run() {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast after work to component " + mToken);
sendFinished(mgr);
}
});
} else {
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to component " + mToken);
//【×3.3.2】如果主线程无等待处理的事件,直接通知 AMS 结果!
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
//【1】如果 mType 为 TYPE_REGISTERED : TYPE_UNREGISTERED,动态接收者,
// 所以会进入 ELSE IF, mOrderedHint 为 true,表示为有序广播,此时我们需要通知系统!!
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to " + mToken);
final IActivityManager mgr = ActivityManagerNative.getDefault();
//【×3.3.2】发送结果!
sendFinished(mgr);
}
}
mOrderedHint
是创建 Args
时赋值初始化的,他是父类 PendingResult
的成员变量,表示该广播是否是有序的!
3.3.2 PendingResult.sendFinished
无论是 Args
,还是 ReceiverData
他们都继承了 PendingResult
类,sendFinished
的实现是在 PendingResult
类中!
通过上面的方法可以看到,静态接收者由于都是有序分发,所以都要回调通知;但是动态接收者,只有广播是有序广播,才会回调通知系统,普通广播是不需要回调系统的!
1 | /** @hide */ |
这里的 mToken
要注意一下:
- 如果是动态注册的接收者,
mToken
是InnerReceiver
对象; - 如果是静态注册的接收者,
mToken
是ApplicationThread
对象;
这里的 mAbortBroadcast
表示是否终止该广播的继续分发,对于有序发送的广播来说,当一个接收者接收到了该广播,可以通过以下方式来设置是否终止该广播的继续传递:
1 | public final void abortBroadcast() { |
以及:1
2
3
4
5public final void clearAbortBroadcast() {
if (mPendingResult != null) {
mPendingResult.mAbortBroadcast = false;
}
}
下面就会进入系统进程!
4 系统进程
通过前面的分析,我们知道以下 2 种广播是通过有序的方式发送的:
- 目标是静态接收者的普通广播;
- 有序广播;
下面我们来看看有序发送的广播的后事处理:
4.1 ActivityManagerS.finishReceiver
参数 IBinder who 表示的是句柄,代表我们广播的目标客户端!
1 | public void finishReceiver(IBinder who, int resultCode, String resultData, |
流程很简单,不多说了!
4.1.1 BroadcastQueue.getMatchingOrderedReceiver
1 | public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) { |
getMatchingOrderedReceiver
逻辑很简单,这里就不多说了!
4.1.2 BroadcastQueue.finishReceiverLocked
接下来看看 finishReceiverLocked 方法做了什么!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
//【1】保存之前的状态!
final int state = r.state;
final ActivityInfo receiver = r.curReceiver;
//【2】初始化广播的一些变量!
r.state = BroadcastRecord.IDLE;
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
//【3】将 r.receiver 置为 null,因为该接收者已经处理完了广播!
r.receiver = null;
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.curReceiver == r) {
r.curApp.curReceiver = null;
}
if (r.curFilter != null) {
r.curFilter.receiverList.curBroadcast = null;
}
r.curFilter = null;
r.curReceiver = null;
r.curApp = null;
//【4】将 mPendingBroadcast 置为 null;
mPendingBroadcast = null;
r.resultCode = resultCode;
r.resultData = resultData;
r.resultExtras = resultExtras;
//【5】判断是否需要终止该广播的继续分发!
// 当 resultAbort 为 true,且广播没有设置 Intent.FLAG_RECEIVER_NO_ABORT 标志位,
// 那么该广播就会终止继续传递!
if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
r.resultAbort = resultAbort;
} else {
r.resultAbort = false;
}
//【6】这里的 waitForServices 传入的是 true,对于后台队列来说 mDelayBehindServices 为 true!
if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
&& r.queue.mOrderedBroadcasts.size() > 0
&& r.queue.mOrderedBroadcasts.get(0) == r) {
ActivityInfo nextReceiver;
if (r.nextReceiver < r.receivers.size()) {
//【6.1】计算下一个接收者!
Object obj = r.receivers.get(r.nextReceiver);
nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
} else {
nextReceiver = null;
}
// 如果本次接受广播的接收者和下一个接收者不在同一个进程,或者下一个接收者为 null
if (receiver == null || nextReceiver == null
|| receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
|| !receiver.processName.equals(nextReceiver.processName)) {
// 对于后台队列中的有序发送的广播,如果广播的目标设别用户下有需要后台延迟启动的服务
// 那就不能继续分发,这里会将广播的状态改为 BroadcastRecord.WAITING_SERVICES,返回 false!
if (mService.mServices.hasBackgroundServices(r.userId)) {
Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());。。。。
r.state = BroadcastRecord.WAITING_SERVICES;
return false;
}
}
}
r.curComponent = null;
//【7】前面我们知道,由于发送广播是异步操作,所以正常情况发送前,状态就改为了下面的状态;
// 这是返回的是 true,那么 doNext 就是 true;如果需要等待 Service 启动,那么状态就为 WAITING_SERVICES;
// 此时 doNext 就为 false;
return state == BroadcastRecord.APP_RECEIVE
|| state == BroadcastRecord.CALL_DONE_RECEIVE;
}
对于静态注册的接收者 r.state 被置为 BroadcastRecord.APP_RECEIVE;
对于动态注册的接收者 r.state 被置为 BroadcastRecord.CALL_DONE_RECEIVE;
这里的 r.queue.mDelayBehindServices
要解释一下,他是在创建前台和后台队列时初始化的:1
2mFgBroadcastQueue = new BroadcastQueue(this, mHandler, "foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler, "background", BROADCAST_BG_TIMEOUT, true);
可以看到,对于后台队列,其 mDelayBehindServices 为 true!表示如果有后台延迟启动的服务,就延迟广播的发送!
那么我们如何判断目标设备用户下是否有后台延迟发送的广播,这就要调用 ActiveServices 的 hasBackgroundServices 方法了:1
2
3
4boolean hasBackgroundServices(int callingUser) {
ServiceMap smap = mServiceMap.get(callingUser);
return smap != null ? smap.mStartingBackground.size() >= mMaxStartingBackground : false;
}
和 Service
的生命周期相关的内容,请去看其他的博客!
最后又再次回到了 processNextBroadcast
方法中了!
5 总结
到这里,sendBroadcast
方法的流程就分析完了,下面我们来总结一下:
5.1 广播的处理流程
在发送广播之前,都需要收集其目标接收者,包括静态接收者和动态接收者!
如果广播设置了 Intent.FLAG_RECEIVER_FOREGROUND
标志位,会被添加到前台队列 mFgBroadcastQueue
中,否则就被添加到后台队列 mBgBroadcastQueue
中!
如果广播设置了 Intent.FLAG_RECEIVER_NO_ABORT
标志位,有序发送的情况下不能被强制中断分发!
5.1.1 普通广播的处理流程
- 1 先发送给动态接收者,并行分发,广播会被加入到
mParallelBroadcasts
集合中,通过一个while
循环,不断遍历mParallelBroadcasts
,直到为空 。- 1.1 设置广播开始分发时间:
r.dispatchTime = SystemClock.uptimeMillis();
和r.dispatchClockTime = System.currentTimeMillis();
- 1.2 设置当前接收者的分发状态:
r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED
- 1.3 对于动态接收者来说,系统进程保存着其
IIntentReceiver.Proxy
对象! - 1.4 通过
app.thread.scheduleRegisteredReceiver
,进入应用进程,调用InnerReceiver.performReceive
拉起广播接收者的onReceive
方法!如果app = null
,就直接通过IIntentReceiver.Proxy.performReceive
,进入应用进程!- **1.4.1** 这时会创建一个 `Args` 对象(继承了 `BroadcastReceiver.PendingResult` 实现了 `Runnable`),通过主线程执行 `Args.run` 方法! - **1.4.2** `PendingResult` 有一个内部成员变量 `mType`,用来保存接收者的类型,对于动态接收者:`mType = TYPE_REGISTERED/TYPE_UNREGISTERED`! - **1.4.3** 最后通过反射,拉起 `onReceive` 方法!
- 1.1 设置广播开始分发时间:
2 然后发送给静态接收者,广播会被加入到
mOrderedBroadcasts
集合中,有序发送!- 2.1 如果已经有一个正在等待目标进程启动的广播
mPendingBroadcast
,那就等待对应进程启动后处理! - 2.2 否则,计算本次该广播要分发的接收者的序号:
recIdx
,和下一个接收者的序号:r.nextReceiver
,二者满足:int recIdx = r.nextReceiver++
2.3 设置广播开始分发时间(当
recIdx
为第一个接收者的时候)r.dispatchTime = r.receiverTime; r.dispatchClockTime = System.currentTimeMillis();
- 设置当前接收者的分发状态:
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
- 设置广播的当前状态:
r.state = BroadcastRecord.APP_RECEIVE;
- 设置广播当前的目标组件:
r.curComponent = component;
- 设置广播当前的目标接收者:
r.curReceiver = info.activityInfo;
- 设置当前接收者的分发状态:
2.4 对于静态接收者,发送广播时候,其进程未必启动,所以要分情况处理:
- **2.4.1** 如果进程未启动,就先启动其进程,并将广播添加到 `mPendingBroadcast` 中,等待进程启动后处理! 进程启动后,会判断当前进程是否有 `mPendingBroadcast`,有的话,就将 `mPendingBroadcast` 发送目标接收者(2.4.2),并设置 `mPendingBroadcast = null;`! </br> - **2.4.2** 如果进程已启动,那就直接发送广播! - 设置广播的目标接收者通信对象:`r.receiver = app.thread.asBinder();`,即目标进程的 `ApplicationThreadProxy` 对象! - 设置广播的目标进程:`r.curApp = app;` - 设置目标进程的当前要接收广播的接收者:`app.curReceiver = r;` - 设置广播的组件信息;`r.intent.setComponent(r.curComponent);` - 通过 `app.thread.scheduleReceiver`,跨进程拉起接收者的 `onReceive` 方法! - 进入应用进程后,会创建一个 `ReceiverData` 对象(其继承了 `BroadcastReceiver.PendingResult`) - `PendingResult` 有一个内部成员变量 `mType`,用来保存接收者的类型,对于静态接收者:`mType = TYPE_COMPONENT`! - 最后通过反射,拉起 `onReceive` 方法!
2.5 最后,调用
ActivityManagerService.finishReceiver
,通知系统进程,接收者完成了广播处理,继续分发!!初始化广播的状态:
r.state = BroadcastRecord.IDLE;
- 清空广播的目标接收者:
Binder
对象:r.receiver = null;
- 清空广播组件信息:
r.intent.setComponent(null);
- 清空广播目标进程的当前接收者:
r.curApp.curReceiver = null;
清空广播过滤器的当前广播:
r.curFilter.receiverList.curBroadcast = null;
清空广播的过滤器:
r.curFilter = null;
- 清空广播接收者信息:
r.curReceiver = null;
- 清空广播的目标进程:
r.curApp = null;
- 清空广播的组件信息:
r.curComponent = null;
- 清空广播的目标接收者:
- 2.1 如果已经有一个正在等待目标进程启动的广播
5.1.2 有序广播的处理流程
对于有序广播来说,会将收集到的静态接收者和动态接收者通过优先级的不同合并到同一个集合中去,然后将该集合添加到指定队列的 mOrderedBroadcasts
集合中!
- 1 如果已经有一个正在等待目标进程启动的广播
mPendingBroadcast
,那就等待对应进程启动后处理,此时不能继续有序发送!
- 2 从
mOrderedBroadcasts
中移除不需要发送的广播,比如:没有接收者,或者所有接收者都已接收了该广播或者被终止继续传递,或者超时了,找到下一个需要分发的广播 r!
- 3 准备工作:
- 计算本次该广播的目标接收者的序号:
recIdx
,和下一个接收者的序号:r.nextReceiver
,二者满足:int recIdx = r.nextReceiver++
; - 初始化该广播的接收时间:
r.receiverTime = SystemClock.uptimeMillis();
,根据该事件设置超时任务! - 设置广播开始分发时间:
r.dispatchTime = r.receiverTime; r.dispatchClockTime = System.currentTimeMillis();
(当recIdx
为第一个接收者的时候)
- 计算本次该广播的目标接收者的序号:
4 从广播的接收者列表中找到
recIdx
对应的目标接收者,接下来就是分发广播了:4.1 如果是动态注册的接收者,
deliverToRegisteredReceiverLocked
!- 4.1.1 设置目标接收者的分发状态:
r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED
4.1.2 设置广播的属性:
- 设置广播接收者对应的
Binder
通信对象:r.receiver = filter.receiverList.receiver.asBinder();
,即IIntentReceiver.Proxy
对象! - 设置广播的过滤器对象:
r.curFilter = filter;
- 设置过滤器处理的广播:
filter.receiverList.curBroadcast = r;
- 设置广播的状态:
r.state = BroadcastRecord.CALL_IN_RECEIVE;
- 设置广播的目标进程:
r.curApp = filter.receiverList.app;
设置目标进程当前处理的广播:
filter.receiverList.app.curReceiver = r;
- 4.1.3 立刻设置广播的状态:
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
表示接收完毕! - 4.1.4 发送广播!
- 4.1.4.1 通过
app.thread.scheduleRegisteredReceiver
,进入应用进程,调用InnerReceiver.performReceive
拉起广播接收者的onReceive
方法!如果app = null
,就直接通过IIntentReceiver.Proxy.performReceive
,进入应用进程! 4.1.4.2 调用
ReceiverDispatcher.performReceive
方法,处理广播!- 这时会创建一个
Args
对象(继承了BroadcastReceiver.PendingResult
实现了Runnable
),通过主线程执行Args.run
方法! PendingResult
有一个内部成员变量mType
,用来保存接收者的类型,对于动态接收者:mType = TYPE_REGISTERED/TYPE_UNREGISTERED
!- 最后通过反射,拉起
onReceive
方法! 调用
ActivityManagerService.finishReceiver
,通知系统进程,接收者完成了广播处理,继续分发!- 初始化广播的状态:
r.state = BroadcastRecord.IDLE;
- 清空广播的目标接收者:Binder 对象:
r.receiver = null;
- 清空广播组件信息:
r.intent.setComponent(null);
- 清空广播目标进程的当前接收者:
r.curApp.curReceiver = null;
- 清空广播目标进程的当前接收者:
清空广播过滤器的当前广播:
r.curFilter.receiverList.curBroadcast = null;
清空广播的过滤器:
r.curFilter = null;
- 清空广播接收者信息:
r.curReceiver = null;
- 清空广播的目标进程:
r.curApp = null;
- 清空广播的组件信息:
r.curComponent = null;
- 初始化广播的状态:
- 这时会创建一个
- 4.1.3 立刻设置广播的状态:
- 设置广播接收者对应的
- 4.1.1 设置目标接收者的分发状态:
- **4.2** 如果是**静态注册的接收者**,`deliverToRegisteredReceiverLocked`!
- **4.1.1** 设置目标接收者的分发状态: `r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED`
- **4.1.2** 设置广播的属性:
- 设置广播的状态:`r.state = BroadcastRecord.APP_RECEIVE;`
- 设置广播的目标组件信息:`r.curComponent = component;`
- 设置广播的目标接收者信息:`r.curReceiver = info.activityInfo;`
- **4.1.3** 根据静态接收者的进程状态来做不同处理:
- **4.1.3.1** 如果进程未启动,就先启动其进程,并将广播添加到 `mPendingBroadcast` 中,等待进程启动后处理!进程启动后,会判断当前进程是否有 `mPendingBroadcast`,有的话,就将 `mPendingBroadcast` 发送目标接收者(4.1.3.2),并设置 `mPendingBroadcast = null;`!
- **4.1.3.2** 如果进程已启动,那就直接发送广播!
- 设置广播的目标接收者通信对象:`r.receiver = app.thread.asBinder();`,即目标进程的 `ApplicationThreadProxy` 对象!
- 设置广播的目标进程:`r.curApp = app;`
- 设置目标进程的当前要接收广播的接收者:`app.curReceiver = r;`
- 设置广播的组件信息;`r.intent.setComponent(r.curComponent);`
- 通过 `app.thread.scheduleReceiver`,跨进程拉起接收者的 `onReceive` 方法!
- 进入应用进程后,会创建一个 `ReceiverData` 对象(其继承了 `BroadcastReceiver.PendingResult`)
- `PendingResult` 有一个内部成员变量 `mType`,用来保存接收者的类型,对于静态接收者:`mType = TYPE_COMPONENT`!
- 最后通过反射,拉起 `onReceive` 方法!
- 调用 `ActivityManagerService.finishReceiver`,通知系统进程,接收者完成了广播处理,继续广播分发!
- 初始化广播的状态:`r.state = BroadcastRecord.IDLE;`
- 清空广播的目标接收者:`Binder` 对象:`r.receiver = null;`
- 清空广播组件信息:`r.intent.setComponent(null);`
- 清空广播目标进程的当前接收者:`r.curApp.curReceiver = null;`
- 清空广播过滤器的当前广播:`r.curFilter.receiverList.curBroadcast = null;`
- 清空广播的过滤器:`r.curFilter = null;`
- 清空广播接收者信息:`r.curReceiver = null;`
- 清空广播的目标进程:`r.curApp = null;`
- 清空广播的组件信息:`r.curComponent = null;`
5.1.3 粘性广播的处理流程
粘性是一个附加的属性,和有序,普通不冲突,普通广播和有序广播都可以是粘性的,粘性广播的一个特性是,系统会将该粘性广播保存下来,以便分发给以后注册的接收者!
保存的集合为:
1 | final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts = |
除去这个特性之外,粘性广播的处理方式和普通广播、有序广播是一样的,这里就不多说了!
- 如果要发送粘性广播,应用必须配置 android.Manifest.permission.BROADCAST_STICKY 权限!
- 对于粘性广播,不能强制广播接收者应该具有某些权限!
- 对于粘性广播,如果有相同的全局粘性广播,会产生冲突,抛出异常!
- Android 7.1.1 上已经不推荐使用粘性广播了,因为会产生安全问题!
5.2 广播的状态周期
对于广播来说,会有如下的几种状态:1
2
3
4
5static final int IDLE = 0;
static final int APP_RECEIVE = 1;
static final int CALL_IN_RECEIVE = 2;
static final int CALL_DONE_RECEIVE = 3;
static final int WAITING_SERVICES = 4;
- 普通广播:
如果是静态注册的接收者:1
BroadcastReocrd.IDLE -> BroadcastReocrd.IDLE.APP_RECEIVE -> BroadcastReocrd.CALL_DONE_RECEIVE ->(BroadcastReocrd.WAITING_SERVICES)-> BroadcastReocrd.IDLE
如果是动态注册的接收者:1
BroadcastReocrd.IDLE
- 有序广播:
如果是静态注册的接收者:1
BroadcastReocrd.IDLE -> BroadcastReocrd.APP_RECEIVE -> BroadcastReocrd.CALL_DONE_RECEIVE ->(BroadcastReocrd.WAITING_SERVICES)-> BroadcastReocrd.IDLE
如果是动态注册的接收者:1
BroadcastReocrd.IDLE -> BroadcastReocrd.CALL_IN_RECEIVE -> BroadcastReocrd.CALL_DONE_RECEIVE ->(BroadcastReocrd.WAITING_SERVICES)-> BroadcastReocrd.IDLE
这里的 BroadcastReocrd.WAITING_SERVICES
只会对后台队列中有序发送的广播有效,因为这些广播可能在等待一些后台服务的启动!
当后台服务启动后,ActiveServices
会通过 rescheduleDelayedStarts
方法拉起这些服务!这里不多说了!
5.3 接收者的分发状态
对于接收者来说,下面这些状态用来表示广播的分发状态:
1 | static final int DELIVERY_PENDING = 0; // 没有用到! |
关于广播的超时,我们会单独一篇博文中介绍!