BroadcastQueueModernImpl 的设计围绕着为设备上的每个潜在进程维护一个单独的 BroadcastProcessQueue 实例。表明用于传送到特定进程的Pending {@link BroadcastRecord} 条目队列。整个类都标记为 {@code NotThreadSafe},因为调用者有责任始终与持有的相关锁进行交互。
在内部,每个队列由一个等待调度的待处理广播和一个当前正在调度的活动广播组成。给等待调度的广播分别维护了紧急、普通、负载三个有序集合,优先级为紧急(3) > 普通(10) > 负载,但是当优先级高的集合被处理的广播数量超过一定限制时,优先级低的广播也有机会得到执行。
每个队列都有一个在未来特定时间“可运行”(runnable at 时间戳)的概念,它支持在每个进程的基础上任意暂停或延迟交付。当它下一次符合执行条件时,该值会受到各种策略的影响,例如:
给定具有有效 runnable at 时间戳的每个进程队列的集合,BroadcastQueueModernImpl 然后愿意将这些 runnable队列提升为 running 状态。 我们根据 runnable at 时间戳的排序顺序选择下一个要提升的每个进程队列,首先选择等待时间最长的进程,旨在减少整体广播调度延迟。
限制
考虑将进程队列中任何其他Pending广播分派到该进程,旨在分批分派以更好地分摊 OOM 调整的成本。每个进程队列一次可最多分派MAX_RUNNING_ACTIVE_BROADCASTS(16,低内存8)个广播,以避免其他进程处于饥饿状态。
收集一次可分发到的receivers时,如果遇到有序广播或静态注册的receiver,则直接中断收集 ,将已有receivers分发到app进程。等app进程完成分发后告知系统,仍然在binder线程继续完成当前进程队列的分发。
仔细关注几种类型的潜在资源匮乏,以及缓解机制:
替代 {@link BroadcastQueue} 实现,它以每个进程为基础调度广播。
每个进程现在都有自己的广播队列,由 {@link BroadcastProcessQueue} 实例表示。 每个队列都有一个在未来特定时间“可运行”的概念,它支持在每个进程的基础上任意暂停或延迟交付。
为了让事情更容易推理,有一种非常强烈的偏好,即让广播交互以这种特定顺序通过一组一致的方法流动:
android U广播机制重构原因:
允许多个进程并行处理广播,并通过一次将多个广播分派给一个进程来最小化 OOm 调整成本。
广播从入队到分发的大致流程,更新mRunning列表在ActivityManager线程。
允许多个进程(4+1)并行处理广播:
所以最多会有5个线程可同时针对5个各自的进程队列进行广播反而分发,大大提高了广播的分发速度(尤其是有序广播或功能清单注册的接收器)。
丢弃和合并频繁发送的广播
广播发送者可以指定如何处理他们的广播
// 下发组策略,表示下发组内的所有广播都需要按原样下发。
@SystemApi
public static final int DELIVERY_GROUP_POLICY_ALL = 0;
// 下发组策略,表示只下发该下发组中最新的广播,其余的可以丢弃。
@SystemApi
public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1;
// 交付组策略,指示交付组中广播的额外数据需要合并到单个广播中,其余数据可以丢弃。
public static final int DELIVERY_GROUP_POLICY_MERGED = 2;
将 MOST_RECENT 策略应用于 CONNECTIVITY_ACTION
阻止处于缓存状态的应用程序使用广播(已废弃,改为阻止冻结状态)
防止处于Cached状态的应用使用广播消耗系统资源
限制cache进程接收广播的更改历史:
/** {@hide} */
@IntDef(prefix = { "DEFERRAL_POLICY_" }, value = {
DEFERRAL_POLICY_DEFAULT,
DEFERRAL_POLICY_NONE,
DEFERRAL_POLICY_UNTIL_ACTIVE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeferralPolicy {}
public @NonNull BroadcastOptions setDeferralPolicy(@DeferralPolicy int deferralPolicy) {
mDeferralPolicy = deferralPolicy;
return this;
}
通过执行如下命令可以查看历史的广播分发记录等信息。
adb shell dumpsys activity broadcasts
// 完整的广播队列列表,如果没有Active状态的广播则不打印当前的进程队列
Per-process queues:
// mRunnableHead 中保存的进程队列(可运行的)
Runnable:
(none)
// mRunning 中保存的进程队列(正在运行的)
Running:
(none)
(none)
(none)
(none)
(none)
// 可暂不管,系统配置的是否忽略传递组策略
Broadcasts with ignored delivery group policies:
{}
// mUidForeground ,保存当前处于前台(procstate为PROCESS_STATE_TOP)的uid
Foreground UIDs:
{}
// 一些常量
Broadcast parameters (key=bcast_fg_constants, observing=true):
bcast_timeout=+10s0ms
bcast_slow_time=+5s0ms
bcast_deferral=+5s0ms
bcast_deferral_decay_factor=0.75
bcast_deferral_floor=0
bcast_allow_bg_activity_start_timeout=+10s0ms
Broadcast parameters (namespace=activity_manager_native_boot):
modern_queue_enabled=true // 使用现代广播队列逻辑处理广播
bcast_max_running_process_queues=4 // 一次最多可同时进行分发的广播队列数量
bcast_max_running_active_broadcasts=16 // 单个广播队列一次最多可分发的广播数量
bcast_max_core_running_blocking_broadcasts=16
bcast_max_core_running_non_blocking_broadcasts=64
bcast_max_pending_broadcasts=256
bcast_delay_normal_millis=+500ms
bcast_delay_cached_millis=+2m0s0ms
bcast_delay_urgent_millis=-2m0s0ms
bcast_delay_foreground_proc_millis=-2m0s0ms
bcast_delay_persistent_proc_millis=-2m0s0ms
bcast_max_history_complete_size=256 // 最多可保存的完整的历史已分发完成的广播数量
bcast_max_history_summary_size=1024 // 最多可保存的简要的历史已分发完成的广播数量
bcast_max_consecutive_urgent_dispatches=3
bcast_max_consecutive_normal_dispatches=10
bcast_core_defer_until_active=true
pending_cold_start_check_interval_millis=30000
// 广播的历史记录
// 正在被分发或等待被分发的广播
Pending broadcasts:
<empty>
// 已经分发完毕的广播(完整的信息)
Historical broadcasts [modern]:
Historical Broadcast modern #0:
// 已经分发完毕的广播(简要的信息)
Historical broadcasts summary [modern]:
#0: act=miui.intent.action.CYCLE_CHECK flg=0x40000010
0 dispatch +5ms finish
enq=2023-10-12 19:04:51.070 disp=2023-10-12 19:04:51.070 fin=2023-10-12 19:04:51.075
@IntDef(flag = false, prefix = { "DELIVERY_" }, value = {
DELIVERY_PENDING, // 初始状态:等待未来运行
DELIVERY_DELIVERED, // 终端状态:成功完成
DELIVERY_SKIPPED, // 终端状态:由于内部政策而跳过
DELIVERY_TIMEOUT, // 终端状态:尝试投递时超时
DELIVERY_SCHEDULED, // 中间状态:当前正在执行
DELIVERY_FAILURE, // 终端状态:派送失败
})
// 设置进程队列中的息屏广播正在等待被分发
ba971da 5456:com.android.settings/1000 not runnable because BLOCKED
runningOomAdjusted:true
e:10 d:8 f:3 fd:1 o:0 a:0 p:8 pd:8 int:0 rt:8 ins:0 m:9 csi:0 adcsi:0 ccu:3 ccn:2
-334ms 67ebdbd android.intent.action.SCREEN_OFF/u-1
PENDING URGENT for registered 9f581c2 // 广播receiver状态为PENDING,广播为urgent(前台)
blocked until 20, currently at 10 of 94 // 有序广播,需要等待前面的广播分发完成
这里针对的是广播发送断
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
pendingIntent.send(options.toBundle());
一般用于标记用户启动的 PendingIntent,需要声明如下权限:
<uses-permission android:name="android.permission.COMPONENT_OPTION_INTERACTIVE" />
Intent intent = new Intent(XXX);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final Bundle mostRecentDeliveryOptions = BroadcastOptions.makeBasic()
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
.toBundle();