本文深入解析 Android 广播(Broadcast)机制的核心原理,结合 Android 6.0 源码,详细阐述广播的注册、发送、接收流程及不同类型广播的处理逻辑。以下用通俗易懂的语言和结构化逻辑进行解读:
一、广播基础:分类与核心组件
1. 广播类型
-
按注册方式分:
- 静态广播:在 AndroidManifest.xml 中通过
<receiver>标签声明,应用安装时由系统注册,无需代码动态干预(如监听系统开机广播)。 - 动态广播:通过
registerReceiver()动态注册,灵活性高,需手动调用unregisterReceiver()注销(如 Activity 中监听网络变化)。
- 静态广播:在 AndroidManifest.xml 中通过
-
按发送方式分:
- 普通广播(sendBroadcast) :并行发送,所有接收者可同时处理,无顺序保证。
- 有序广播(sendOrderedBroadcast) :串行发送,接收者按优先级依次处理,前一个接收者可截断广播或传递结果。
- 粘性广播(sendStickyBroadcast) :广播发送后持续存在,新注册的接收者仍可获取历史广播数据(需权限
BROADCAST_STICKY)。
2. 核心组件
BroadcastReceiver:接收广播的组件,需实现onReceive()处理逻辑。ContentResolver:客户端发送广播的入口,通过sendBroadcast()等方法触发。ActivityManagerService(AMS):系统核心服务,管理广播的注册、发送队列及进程间通信。BroadcastQueue:分为前台队列(mFgBroadcastQueue)和后台队列(mBgBroadcastQueue),分别处理不同优先级的广播。BroadcastRecord:记录广播的元数据(如发送者、接收者、时间戳),用于队列管理。
二、广播注册流程:动态与静态的差异
1. 动态广播注册(registerReceiver)
-
客户端调用:
在 Activity/Service 中通过ContextImpl.registerReceiver发起注册,传入广播接收者BroadcastReceiver和过滤条件IntentFilter。 -
创建分发器:
LoadedApk.getReceiverDispatcher创建ReceiverDispatcher,内部包含InnerReceiver(继承自IIntentReceiver.Stub,作为 Binder 服务端)。InnerReceiver通过 Binder 将注册信息传递给 AMS。
-
AMS 处理:
- 在
AMS.registerReceiver中,将动态注册的广播保存到mRegisteredReceivers(以ReceiverList记录),并添加到mReceiverResolver供查询。 - 若为粘性广播,立即从
mStickyBroadcasts中获取历史数据并分发。
- 在
2. 静态广播注册
- 应用安装时,系统通过
PackageManager解析 Manifest 中的<receiver>标签,注册信息存储在 AMS 的mReceiverResolver中,无需手动代码干预。 - 发送广播时,AMS 通过
collectReceiverComponents查询静态注册的接收者(ResolveInfo),与动态注册的接收者(BroadcastFilter)合并处理。
三、广播发送流程:普通、有序、粘性的区别
1. 通用流程
-
客户端触发:
通过Context.sendBroadcast()等方法发送 Intent,携带广播类型(普通 / 有序 / 粘性)和参数。 -
AMS 预处理:
- 验证权限、添加系统标志(如
FLAG_EXCLUDE_STOPPED_PACKAGES排除已停止应用)。 - 处理系统广播(如包安装、时间变化)和粘性广播(保存到
mStickyBroadcasts)。
- 验证权限、添加系统标志(如
-
队列分发:
- 普通广播:加入
mParallelBroadcasts队列,并行分发给所有动态注册的接收者(BroadcastFilter)。 - 有序广播:加入
mOrderedBroadcasts队列,按优先级串行分发给所有接收者(静态 + 动态)。 - 粘性广播:同时保存到
mStickyBroadcasts,新注册的接收者可获取。
- 普通广播:加入
2. 关键差异
| 类型 | 队列 | 处理方式 | 接收者类型 | 超时机制 |
|---|---|---|---|---|
| 普通广播 | mParallel | 并行,一次性分发 | 仅动态注册 | 无 |
| 有序广播 | mOrdered | 串行,按优先级依次处理 | 静态 + 动态 | 总超时 = 2×N× 超时时间(N 为接收者数) |
| 粘性广播 | 单独存储 | 持续存在,新注册者可获取历史数据 | 所有注册者 | 无 |
四、广播处理流程:从队列到接收者回调
1. 队列调度(scheduleBroadcastsLocked)
- AMS 通过
BroadcastQueue的scheduleBroadcastsLocked发送消息BROADCAST_INTENT_MSG,触发BroadcastHandler处理队列。 - 并行广播处理:
从mParallelBroadcasts中取出所有BroadcastRecord,逐个分发给动态注册的BroadcastFilter,通过deliverToRegisteredReceiverLocked触发回调。 - 有序广播处理:
从mOrderedBroadcasts中取出首个BroadcastRecord,按顺序分发给静态和动态接收者,支持中途截断(resultAbort)或传递结果(resultCode)。
2. 跨进程通信(IPC)
- 动态接收者:
通过InnerReceiver(Binder 服务端)的performReceive方法,将广播事件通过 Handler 发送到接收者所在进程的主线程,触发onReceive回调。 - 静态接收者:
若接收者所在进程未启动,AMS 通过startProcessLocked创建进程,调用scheduleReceiver触发回调。
3. 超时机制
-
有序广播存在超时风险:
- 单个接收者处理时间超过
mTimeoutPeriod(前台 10 秒,后台 60 秒)。 - 总处理时间超过
2 × N × mTimeoutPeriod(N 为接收者数量),触发 ANR。
- 单个接收者处理时间超过
-
普通广播和粘性广播无超时,因并行处理或无需等待。
五、核心机制与开发注意事项
1. 粘性广播的特殊性
- 发送后存储在
mStickyBroadcasts,新注册的接收者通过registerReceiver立即获取历史数据(返回值为该粘性 Intent)。 - 需谨慎使用,避免内存泄漏(及时调用
removeStickyBroadcast清除)。
2. 动态广播的生命周期管理
- 必须在合适的生命周期阶段注销(如 Activity 的
onDestroy),否则可能导致内存泄漏或空指针异常。 - 使用弱引用或全局单例管理
BroadcastReceiver,避免持有 Activity 引用。
3. 有序广播的优先级控制
- 通过
IntentFilter的android:priority属性(-1000~1000,值越大优先级越高)控制执行顺序。 - 高优先级接收者可通过
abortBroadcast()截断广播,或通过setResult()传递结果给后续接收者。
4. 性能优化
- 避免在
onReceive中执行耗时操作(如网络请求、数据库写入),可通过IntentService或 WorkManager 处理异步任务。 - 减少静态广播的使用,避免应用启动时触发大量注册,影响启动速度。
六、总结:流程时序与核心要点
1. 时序图(动态广播接收)
plaintext
客户端进程 AMS进程 接收者进程
───────────────┬───────────────┬───────────────
1. registerReceiver() │ │
2. 创建InnerReceiver │ │
3. AMP.registerReceiver() │ │
4. AMS保存ReceiverList │ │
5. sendBroadcast() │ │
6. AMP.broadcastIntent() │ │
7. AMS入队广播到队列 │ │
8. BroadcastHandler处理 │ │
9. deliverToRegisteredReceiverLocked() │
10. ATP.scheduleRegisteredReceiver() │ 10. 接收者Binder线程
11. InnerReceiver.performReceive() │ 11. 发送Handler消息
12. 主线程调用onReceive() │ 12. 回调onReceive()
───────────────┴───────────────┴───────────────
2. 核心要点
-
Binder 是基础:广播的跨进程通信依赖 Binder 机制,
InnerReceiver和ApplicationThread是通信桥梁。 -
队列决定顺序:普通广播并行处理动态接收者,有序广播串行处理所有接收者,静态接收者始终串行。
-
生命周期管理:动态广播需手动注销,粘性广播需主动清除,避免资源泄漏。
通过理解广播机制的底层流程,开发者可更精准地选择广播类型,优化性能,避免常见问题(如 ANR、内存泄漏),确保应用在跨进程通信场景下的稳定性和效率。