Android 广播机制浅析【2】发送广播

本分对广播发送的流程进行分析,主要包括应用端发送广播到广播入队的过程,涉及从发送者进程到 SystemServer 进程的通信。

应用端发送广播

Context 中提供了 sendBroadcast 和 sendOrderedBroadcast 来发送无序和有序广播,最终都是通过 Binder调用了 ActivityManagerService#broadcastIntentLocked,从发送广播应用进程走到了 SystemServer 进程

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
final int broadcastIntentLocked(...) {
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); //排除已停止运行应用
//如果系统还未启动完成,则不主动启动静态注册的应用,即此时只会处理动态注册
if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
//1、处理黏性广播
// Add to the sticky list if requested.
if (sticky) {
// 检查 BROADCAST_STICKY 权限,没有则抛出 SecurityException
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
callingPid, callingUid)
!= PackageManager.PERMISSION_GRANTED) {...}
// 检查是否有设置权限,如果有则直接返回
if (requiredPermissions != null && requiredPermissions.length > 0) {...}
//不允许指定 Component,抛出 SecurityException 异常
if (intent.getComponent() != null) {...}
// 检查是否 USER_ALL 下已经存在该黏性广播,不存在则添加
if (userId != UserHandle.USER_ALL) {...}
//获取该 userId 下的所有黏性广播,已经存在则替换,不存在则添加
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
...
}//处理黏性广播结束

//2、查找所有的广播接收者
List receivers = null; //所有匹配的静态注册接收者
List<BroadcastFilter> registeredReceivers = null; //所有匹配的动态注册接收者
// 如果没有设置 FLAG_RECEIVER_REGISTERED_ONLY,则去 PMS 中查询所有静态注册的接收者
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
receivers = collectReceiverComponents(...);
}
//2.1 处理无序广播
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
...
final BroadcastQueue queue = broadcastQueueForIntent(intent); //获取指定的广播队列
BroadcastRecord r = new BroadcastRecord(...);
if (!replaced) {
//添加到并行广播队列并开始分发
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null; //置空
NR = 0;
}//处理无序广播end

//2.2 处理有序广播
// 先合并所有的接收者到 receivers 中
if ((receivers != null && receivers.size() > 0) || resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);//获取指定的广播队列
BroadcastRecord r = new BroadcastRecord(...);
//添加到有序广播队列并开始分发
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
return ActivityManager.BROADCAST_SUCCESS; //发送广播成功
}

保存黏性广播

当参数 sticky 为 true 时表示发送黏性广播,可以通过 Context#sendStickyBroadcast 发送,需要注册 Manifest.permission.BROADCAST_STICKY 权限,由于具有安全性问题,已经被标记为弃用,因此不推荐使用这种方式。在 ActivityManagerService 中 mStickyBroadcasts 用来保存所有用户当前活跃的黏性广播:

1
2
final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

获取广播队列

无论是处理有序广播还是无序广播,首先都要获取到对应的广播队列:

1
2
3
4
5
BroadcastQueue broadcastQueueForIntent(Intent intent) {
...
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}

这里会根据是否有设置 FLAG_RECEIVER_FOREGROUND 属性来返回前台广播队列 mFgBroadcastQueue 或是后台广播队列 mBgBroadcastQueue,它们都是 BroadcastQueue 类型,在 AMS 的构造函数中初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;

public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
mHandlerThread = new ServiceThread(TAG, THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
// Broadcast policy parameters
final BroadcastConstants foreConstants = new BroadcastConstants(
Settings.Global.BROADCAST_FG_CONSTANTS);
foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT; //10*1000 ms, 10s
final BroadcastConstants backConstants = new BroadcastConstants(
Settings.Global.BROADCAST_BG_CONSTANTS);
backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT; //60*1000 ms, 60s
mFgBroadcastQueue = new BroadcastQueue(this, mHandler, "foreground", foreConstants,false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler, "background", backConstants,true);
}

可以看到前台广播和后台广播在超时上的区别,分别为 10s 和 60s,在构造 BroadcastQueue 时这里传入的是 AMS 中名为 ActivityManager 的 HandlerThread ,因此 BroadcastQueue 中处理消息都是在这个线程中:

1
2
3
4
5
6
7
8
9
10
BroadcastQueue(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
mService = service;
mHandler = new BroadcastHandler(handler.getLooper());
mQueueName = name;
mDelayBehindServices = allowDelayBehindServices;

mConstants = constants;
mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
}

这里还需要注意下的是 mDelayBehindServices,在后台广播中为 true,这个变量主要用于控制是否在应用存在多个后台服务时延迟对广播的继续派发,后面会说到。

广播入队

无序广播通过调用 BroadcastQueue#enqueueParallelBroadcastLocked 来将广播对象 BroadcastRecord 插入到队列中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
mParallelBroadcasts.add(r);
enqueueBroadcastHelper(r);
}

private void enqueueBroadcastHelper(BroadcastRecord r) {
r.enqueueClockTime = System.currentTimeMillis(); // 设置广播的入队时间
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { // 记录 systrace
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
}
}

private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
return String.format("Broadcast %s from %s (%s) %s",...);
}

无序广播队列中通过 ArrayList 来保存广播对象,有序广播是通过 BroadcastDispatcher 来完成广播的分发,广播对象也是保存在 ArrayList 中:

1
2
3
4
5
6
7
8
9
10
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}

//com.android.server.am.BroadcastDispatcher
private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
mOrderedBroadcasts.add(r);
}