第二板块:Android 四大组件标准化学理 | 第九篇:BroadcastReceiver 事件分发与有序广播
所属板块:第二板块 --- Android 四大组件标准化学理
前置知识:第八篇中的 Service 优先级、Binder IPC、AMS 调度机制
本篇定位 :BroadcastReceiver 是 Android 系统中最轻量级、最松散耦合 的组件。它不提供 UI,也不直接执行长时间任务,而是作为一个系统级事件的中转站(Event Hub) 。本篇将彻底拆解广播的异步分发模型 、AMS 中的队列管理机制 、静态注册与动态注册的权限鸿沟 、有序广播(Ordered Broadcast)的拦截与修改机制 、粘性广播(Sticky Broadcast)的历史遗留问题 、LocalBroadcastManager 的进程内通信本质 。我们将深入 ActivityManagerService (AMS) 与 BroadcastQueue 的源码级逻辑,揭示广播为何被称为"冷启动炸弹"以及如何正确规避。全程无业务代码、无最佳实践建议,仅保留 Android Framework 的底层定义与系统级调度规范。
1. 核心结论先行(Thesis Statement)
BroadcastReceiver 是 Android 操作系统中用于接收系统或应用发出的异步消息(Intent)的组件。它不是线程 ,也不是服务 ,而是一个由系统服务(AMS)管理的、具有极短生命周期的、运行在主线程中的回调实体。
- Receiver 的本质 :一个事件监听器(Event Listener) 。它监听特定的
Intent广播,并在接收到广播时执行一段简短的逻辑。 - 分发模型 :异步发布-订阅模型(Pub-Sub)。发送者(Publisher)发出广播,AMS 作为中介(Broker)将广播分发给所有符合条件的接收者(Subscriber)。
- 生命周期悖论 :Receiver 的
onReceive()方法执行完毕后,系统会立即销毁 Receiver 实例。它不能执行耗时操作,否则会阻塞主线程导致 ANR。 - 注册鸿沟 :静态注册 (Manifest)由系统唤醒,即使应用未运行也能接收;动态注册(Context.registerReceiver)依附于组件生命周期,组件销毁后失效。
2. BroadcastReceiver 的两种注册范式
2.1 静态注册(Manifest-declared)
在 AndroidManifest.xml 中声明 Receiver。这是系统级的注册方式。
学术定义:
- 常驻性:Receiver 信息在应用安装时由 PMS(PackageManagerService)解析并注册到系统中。
- 唤醒能力:即使应用进程未运行,当广播到达时,AMS 会通过 Zygote 启动应用进程并实例化 Receiver。
- 权限控制 :可以通过
android:permission限制谁能发送广播给我。
Manifest 示例:
xml
<receiver
android:name=".BootCompletedReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
</intent-filter>
</receiver>
2.2 动态注册(Context-registered)
在代码中通过 Context.registerReceiver() 注册。这是应用级的注册方式。
学术定义:
- 临时性:Receiver 的生命周期与注册它的 Context(通常是 Activity 或 Service)绑定。
- 灵活性:可以随时注册和注销,适合监听与 UI 状态相关的广播。
- 无唤醒能力:如果应用进程已死,动态注册的 Receiver 无法接收广播。
代码示例(系统规范):
java
public class MainActivity extends Activity {
private MyReceiver receiver;
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(receiver, filter);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(receiver); // 必须注销,否则内存泄漏
}
}
2.3 两种注册方式的对比
| 维度 | 静态注册 | 动态注册 |
|---|---|---|
| 注册地点 | AndroidManifest.xml | Java/Kotlin 代码 |
| 生命周期 | 独立于应用进程 | 依附于 Context |
| 唤醒能力 | 能(冷启动应用) | 不能 |
| 执行时机 | 系统随时可能唤醒 | 仅在应用运行时 |
| 适用场景 | 系统事件(开机、电量、网络) | 应用内事件(UI 更新) |
3. 广播的分类与分发算法
3.1 普通广播(Normal Broadcast)
异步发送,所有 Receiver 同时接收,无法被拦截或修改。
学术定义:
- 并行分发:AMS 将广播发送给所有符合条件的 Receiver,不保证顺序。
- 不可控:发送者无法知道 Receiver 是否接收成功,也无法修改数据。
3.2 有序广播(Ordered Broadcast)
同步发送,Receiver 按优先级依次接收,可以拦截或修改数据。
学术定义:
- 串行分发 :Receiver 按
android:priority从高到低依次执行。 - 拦截机制 :高优先级的 Receiver 可以调用
abortBroadcast()阻止广播继续向下传递。 - 数据修改 :Receiver 可以通过
setResultExtras()修改数据,供下一个 Receiver 使用。
Manifest 示例:
xml
<receiver android:name=".HighPriorityReceiver">
<intent-filter android:priority="1000">
<action android:name="com.example.MY_ORDERED_BROADCAST" />
</intent-filter>
</receiver>
<receiver android:name=".LowPriorityReceiver">
<intent-filter android:priority="-1000">
<action android:name="com.example.MY_ORDERED_BROADCAST" />
</intent-filter>
</receiver>
3.3 粘性广播(Sticky Broadcast)【已废弃】
Android 5.0 (L) 起废弃。它允许广播"粘"在系统上,后来注册的 Receiver 也能收到之前的广播。
学术定义:
- 历史缓存:AMS 会缓存最后一条粘性广播。
- 安全风险:任何应用都可以接收敏感信息,已被禁止。
4. 广播的分发流程(AMS 源码级解析)
4.1 广播发送流程
接收者进程 BroadcastQueue ActivityManagerService 发送者进程 接收者进程 BroadcastQueue ActivityManagerService 发送者进程 #mermaid-svg-BuRuN4GJdqFowPhr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BuRuN4GJdqFowPhr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BuRuN4GJdqFowPhr .error-icon{fill:#552222;}#mermaid-svg-BuRuN4GJdqFowPhr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BuRuN4GJdqFowPhr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BuRuN4GJdqFowPhr .marker.cross{stroke:#333333;}#mermaid-svg-BuRuN4GJdqFowPhr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BuRuN4GJdqFowPhr p{margin:0;}#mermaid-svg-BuRuN4GJdqFowPhr .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BuRuN4GJdqFowPhr text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BuRuN4GJdqFowPhr .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-BuRuN4GJdqFowPhr .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr .sequenceNumber{fill:white;}#mermaid-svg-BuRuN4GJdqFowPhr #sequencenumber{fill:#333;}#mermaid-svg-BuRuN4GJdqFowPhr #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr .messageText{fill:#333;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BuRuN4GJdqFowPhr .labelText,#mermaid-svg-BuRuN4GJdqFowPhr .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .loopText,#mermaid-svg-BuRuN4GJdqFowPhr .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BuRuN4GJdqFowPhr .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-BuRuN4GJdqFowPhr .noteText,#mermaid-svg-BuRuN4GJdqFowPhr .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BuRuN4GJdqFowPhr .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BuRuN4GJdqFowPhr .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BuRuN4GJdqFowPhr .actorPopupMenu{position:absolute;}#mermaid-svg-BuRuN4GJdqFowPhr .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-BuRuN4GJdqFowPhr .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BuRuN4GJdqFowPhr .actor-man circle,#mermaid-svg-BuRuN4GJdqFowPhr line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-BuRuN4GJdqFowPhr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} sendBroadcast(intent)检查权限 (checkBroadcastFromSystem)enqueueParallelBroadcastLocked() / enqueueOrderedBroadcastLocked()构建 BroadcastRecordscheduleReceiver() (Binder IPC)实例化 ReceiveronReceive()finishReceiver()
4.2 BroadcastQueue 的内部机制
AMS 中有两个 BroadcastQueue:
- Foreground Broadcast Queue:前台广播,优先级高,超时时间短(10秒)。
- Background Broadcast Queue:后台广播,优先级低,超时时间长(60秒)。
源码解析:
java
// ActivityManagerService.java
public void broadcastIntent(...) {
// 1. 检查调用者权限
enforceNotIsolatedCaller("broadcastIntent");
// 2. 处理粘性广播
if (sticky) {
// ...
}
// 3. 选择队列
BroadcastQueue queue = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0
? mFgBroadcastQueue : mBgBroadcastQueue;
// 4. 入队
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
4.3 BroadcastRecord 数据结构
java
// com.android.server.am.BroadcastRecord
class BroadcastRecord extends Binder {
final Intent intent; // 广播 Intent
final ProcessRecord callerApp; // 发送者进程
final String[] requiredPermissions; // 接收者需要的权限
final int receiversSize; // 接收者数量
final ArrayList<BroadcastFilter> receivers; // 动态接收者
final ResolveInfo[] resolveInfo; // 静态接收者
int nextReceiver; // 下一个要处理的接收者索引(有序广播用)
boolean ordered; // 是否是有序广播
}
5. 广播的安全性机制
5.1 发送权限(Send Permission)
发送广播时可以指定权限,只有拥有该权限的应用才能接收。
java
// 发送者
sendBroadcast(intent, "com.example.permission.MY_PERMISSION");
// 接收者 Manifest
<uses-permission android:name="com.example.permission.MY_PERMISSION" />
5.2 接收权限(Receive Permission)
在 Manifest 中声明 Receiver 时指定权限,只有拥有该权限的发送者才能发送广播给我。
xml
<receiver
android:name=".MyReceiver"
android:permission="com.example.permission.ANOTHER_PERMISSION">
<intent-filter>
<action android:name="com.example.MY_ACTION" />
</intent-filter>
</receiver>
5.3 LocalBroadcastManager(进程内安全广播)
LocalBroadcastManager 是 Support Library 提供的进程内广播 工具。它不使用 Binder IPC,也不通过 AMS,而是直接在应用进程内分发。
学术定义:
- 安全性:广播数据不会离开应用进程,不会被其他应用截获。
- 效率:避免了 IPC 开销,速度更快。
- 现状 :已被
LiveData或RxJava等响应式编程库取代,但在理解广播机制时仍具学术价值。
6. 广播的陷阱与系统限制
6.1 ANR 风险
onReceive() 运行在主线程,且只有 10 秒 (前台广播) 或 60 秒(后台广播)的执行时间。
学术定义:
- ANR(Application Not Responding) :如果
onReceive()执行超过规定时间,系统会弹出 ANR 对话框。 - 禁忌 :绝对不能在
onReceive()中执行网络请求、数据库读写、文件 IO 或睡眠操作。
6.2 广播延迟与排队
当系统繁忙时,广播可能会被延迟。特别是在 Android 8.0 (O) 之后,系统对后台广播进行了严格限制。
Android 8.0+ 的限制:
- 后台执行限制:应用在后台时,无法注册隐式广播的静态 Receiver(部分系统广播除外,如开机广播)。
- 原因:防止应用滥用广播在后台唤醒,消耗电量。
6.3 替代方案
由于广播的局限性,现代 Android 开发推荐使用以下方案:
| 场景 | 推荐方案 | 学术理由 |
|---|---|---|
| 应用内通信 | LiveData / RxJava / Flow |
响应式编程,解耦更彻底,无 IPC 开销。 |
| 跨进程通信 | Messenger / AIDL |
需要双向通信或复杂数据结构时。 |
| 后台任务 | WorkManager / JobScheduler |
系统调度,省电,符合后台限制。 |
| 系统事件 | 静态注册广播 | 唯一合法场景,如开机、电量变化。 |
7. 关键源码解析:广播的匹配算法
AMS 如何找到符合条件的 Receiver?
java
// PackageManagerService.java
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
// 1. 查询静态 Receiver (Manifest)
List<ResolveInfo> receivers = mReceivers.queryIntent(intent, resolvedType, flags, userId);
// 2. 过滤权限
for (ResolveInfo ri : receivers) {
if (!grantImplicitAccess(userId, ri, uid, owningUid, false)) {
continue; // 权限不足,过滤掉
}
}
return receivers;
}
8. 广播的冷启动效应与性能影响
8.1 冷启动炸弹(Cold Start Bomb)
静态注册的 Receiver 具有唤醒能力,这意味着一个广播可以启动一个已经死亡的应用进程。
学术定义:
- 进程创建开销:每次唤醒都需要 Zygote fork 新进程,加载 ART 虚拟机,初始化 Application。
- 累积效应:如果多个应用都监听同一个高频广播(如网络变化),会导致系统频繁创建和销毁进程,造成严重的性能损耗和电量消耗。
8.2 广播的序列化执行
即使是普通广播,在同一个应用进程中,Receiver 的 onReceive() 也是串行执行的。如果一个 Receiver 执行时间过长,会阻塞后续 Receiver 的执行。
9. 关键数据结构与源码定义(续)
9.1 BroadcastFilter(动态注册记录)
java
// com.android.server.am.BroadcastFilter
class BroadcastFilter extends IntentFilter {
final String packageName;
final String receiverClassName;
final int owningUid;
final int owningPid;
final boolean instantApp;
}
9.2 ReceiverList(Receiver 列表)
java
// com.android.server.am.ReceiverList
class ReceiverList extends ArrayList<BroadcastFilter> {
final ProcessRecord app; // 所属进程
final int pid; // 进程 ID
final int uid; // 用户 ID
final String packageName; // 包名
}
10. 本篇总结(Knowledge Closure)
| 关键点 | 纯学术定义 |
|---|---|
| Receiver 的本质 | 由 AMS 管理的事件监听器,生命周期极短,运行在主线程。 |
| 注册范式 | 静态注册(系统级,可唤醒)与动态注册(应用级,依附生命周期)。 |
| 分发模型 | 异步 Pub-Sub 模型,有序广播提供拦截和修改能力。 |
| 安全性 | 通过发送权限和接收权限实现双向保护。 |
| 系统限制 | Android 8.0+ 严格限制后台静态广播,防止滥用。 |
| 性能陷阱 | 冷启动开销大,执行时间长会导致 ANR。 |
下一篇预告 :第二板块:Android 四大组件标准化学理 | 第十篇:ContentProvider 数据共享与 SQLite 引擎