核心思想:发布/订阅模式 (Publish/Subscribe)
想象一下现实中的广播电台:
- 电台 (发送者 - Sender) :发布一条消息(比如“明天有暴雨”)。
- 收音机 (接收者 - Receiver) :需要调到特定频道并且打开开关才能收到这个消息。
- 消息 (广播 - Broadcast) :电台发出的具体内容(“明天有暴雨”)。
Android 广播机制非常类似:
- 发送者 (Broadcast Sender) :一个组件(Activity, Service, 甚至是另一个 BroadcastReceiver)发出一个广播事件 (Broadcast) 。
- 广播 (Broadcast) :一个携带了特定信息和意图(Intent)的对象。这个 Intent 包含了动作 (Action) 来标识广播的类型(比如
"android.intent.action.BATTERY_LOW")。 - 接收者 (Broadcast Receiver) :一个组件,专门负责监听 (订阅) 特定类型的广播(通过 IntentFilter 指定 Action 等条件)。当匹配的广播发出时,系统会调用它的
onReceive()方法。 - 系统 (Android OS) :扮演“广播塔”的角色,负责将发出的广播分发给所有注册监听该类型广播的接收者。
关键概念详解
-
Broadcast (广播事件):
-
本质是一个
Intent对象。 -
核心属性:
- Action (动作): 最重要的标识符!是一个字符串,表示发生了什么事件(例如:
Intent.ACTION_BOOT_COMPLETED- 系统启动完成,"com.example.myapp.MY_CUSTOM_ACTION"- 自定义事件)。 - Data (数据): 可选的 URI 数据。
- Category (类别): 可选的附加信息。
- Extras (附加数据): 一个
Bundle对象,可以携带任意自定义的键值对数据。
- Action (动作): 最重要的标识符!是一个字符串,表示发生了什么事件(例如:
-
类型:
- 显式广播 (Explicit Broadcast): 明确指定了目标接收者组件的类名。通常用于应用内部通信(因为你知道具体要发给谁)。
- 隐式广播 (Implicit Broadcast): 只指定了 Action(可能还有 Data/Category),不指定具体接收者。系统会寻找所有注册监听该 Action 的接收者。常用于系统事件或应用间通信(例如通知电量低、网络变化)。
-
-
BroadcastReceiver (广播接收者):
-
一个继承自
BroadcastReceiver的类。 -
核心方法:
void onReceive(Context context, Intent intent)- 当匹配的广播到达时,系统自动调用此方法。
context: 当前上下文。intent: 接收到的广播 Intent,包含所有发送的信息。
-
重要限制:
onReceive()方法运行在主线程 (UI 线程) !- 在
onReceive()中不能执行耗时操作(超过 10 秒会导致 ANR - Application Not Responding)。 - 如果需要耗时操作,必须启动一个
Service或使用JobScheduler/WorkManager。
-
-
IntentFilter (意图过滤器):
-
一个对象,用于声明一个
BroadcastReceiver对哪些广播感兴趣。 -
主要指定过滤条件:
<action>: 必须匹配的 Action 字符串(至少一个)。<data>: (可选)匹配特定的 URI 模式和 MIME 类型。<category>: (可选)匹配特定的类别。
-
作用: 告诉系统,“我只想接收带有这些 Action/Data/Category 的广播”。
-
-
注册方式 (如何让收音机“调频”并“开机”):
-
静态注册 (Manifest-declared):
-
在
AndroidManifest.xml文件中声明<receiver>标签。 -
特点:
- 接收者长期存活,即使 App 未启动(只要系统在运行且事件发生)。
- 主要用来监听系统全局事件(开机完成、时区改变、安装应用等)。
- Android 8.0 (API 26+) 重大限制: 出于性能和电量考虑,大多数隐式广播不能再在 Manifest 中静态注册(除了少数系统豁免的白名单广播)。必须使用动态注册。
-
示例 (
AndroidManifest.xml):<receiver android:name=".MyBootReceiver" android:exported="true"> <!-- 是否允许其他应用发送广播给它 --> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
-
-
动态注册 (Context-registered):
-
在代码中(通常在 Activity 或 Service 的
onCreate()/onResume())使用Context.registerReceiver()方法注册。 -
在对应的地方(如
onDestroy()/onPause())使用Context.unregisterReceiver()注销。 -
特点:
- 接收者生命周期与注册它的
Context(如 Activity)绑定。Context 销毁时,接收者自动失效。避免内存泄漏的关键! - 适用于应用内部或特定界面/服务需要时才监听的广播。
- 不受 Android 8.0 隐式广播限制影响(因为接收者生命周期可控)。
- 接收者生命周期与注册它的
-
示例 (Activity 中):
public class MyActivity extends AppCompatActivity { private MyDynamicReceiver mReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // 1. 创建接收者实例 mReceiver = new MyDynamicReceiver(); // 2. 创建 IntentFilter,指定要监听的 Action IntentFilter filter = new IntentFilter(); filter.addAction("com.example.myapp.MY_DYNAMIC_ACTION"); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); // 监听网络变化 // 3. 动态注册 registerReceiver(mReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); // 4. 非常重要!在 Context 销毁时注销接收者 unregisterReceiver(mReceiver); } // 内部类定义动态接收者 private class MyDynamicReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ("com.example.myapp.MY_DYNAMIC_ACTION".equals(action)) { // 处理自定义广播 } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { // 处理网络变化广播 } } } }
-
-
-
发送广播 (如何“发布电台消息”):
-
使用
Context.sendBroadcast(),Context.sendOrderedBroadcast(), 或Context.sendStickyBroadcast()(已废弃) 方法。 -
普通广播 (Normal Broadcast -
sendBroadcast()):- 最常见的发送方式。
- 异步发送给所有匹配的接收者。
- 接收者没有执行顺序,也无法中断广播的传递。
-
有序广播 (Ordered Broadcast -
sendOrderedBroadcast()):-
接收者按照优先级 (
android:priority属性或在IntentFilter中设置) 依次接收广播。 -
高优先级的接收者先收到,可以:
- 修改广播内容 (Result Extras): 通过
setResultExtras(Bundle)等传递数据给下一个接收者。 - 中断广播 (Abort): 调用
abortBroadcast()阻止广播继续传递给后面的低优先级接收者。
- 修改广播内容 (Result Extras): 通过
-
常用于需要处理链或需要最终结果的场景(例如“电池状态收集”)。
-
-
粘性广播 (Sticky Broadcast -
sendStickyBroadcast()):- 已废弃 (Deprecated)! Android 5.0 (API 21) 开始废弃,Android 13 (API 33) 彻底移除。
- 发送后,广播会驻留在系统中。之后注册的、匹配该广播的接收者会立即收到最后一次发送的粘性广播。
- 常用于需要获取最新状态(如最新电量)的场景,但现在推荐使用其他机制(如
SharedPreferences,LiveData, 直接查询系统服务如BatteryManager)。
-
-
本地广播 (Local Broadcast):
-
使用
LocalBroadcastManager(现在也推荐使用LiveData或 EventBus 替代)。 -
核心特点:
- 广播的发送和接收只在同一个 App 进程内进行。
- 更安全: 其他 App 无法发送或接收这些广播。
- 更高效: 避免了进程间通信 (IPC) 的开销。
-
用法 (已弃用 API,仅作理解):
// 获取 LocalBroadcastManager 实例 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); // 注册接收者 (类似动态注册,但用 lbm) lbm.registerReceiver(receiver, filter); // 发送本地广播 lbm.sendBroadcast(intent); // 注销 lbm.unregisterReceiver(receiver);
-
使用广播的典型场景
- 监听系统事件: 开机完成、时区改变、电池电量低、屏幕开/关、网络连接变化、耳机插入拔出、安装/卸载应用、短信接收 (需要权限)、电话状态 (需要权限)。
- 应用内部通信: 一个后台服务完成任务后通知前台 Activity 更新 UI (通常结合动态注册和本地广播/LiveData)。不同组件之间传递消息。
- 应用间通信 (受限): 发送一个自定义广播,其他应用如果有接收者监听该 Action 就能收到。但由于权限和安全问题,实际应用场景不如系统广播广泛。需要谨慎设计权限 (
android:permission) 来控制谁可以发送/接收。
总结-使用
-
广播 = 消息 (Intent) ,由发送者发出。
-
接收者 (BroadcastReceiver) 通过
IntentFilter声明自己关心哪些广播。 -
注册方式:
- 静态注册 (Manifest): 长期监听系统事件 (Android 8.0+ 受限)。
- 动态注册 (代码): 灵活监听,生命周期与 Context 绑定,必须记得注销!
-
发送方式:
- 普通广播 (
sendBroadcast) :异步发给所有接收者。 - 有序广播 (
sendOrderedBroadcast) :按优先级传递,可修改/中断。 - (已废弃) 粘性广播 (
sendStickyBroadcast) :驻留系统,新接收者立得。
- 普通广播 (
-
本地广播: 进程内高效安全通信 (推荐用
LiveData等替代)。 -
onReceive()限制: 主线程,禁耗时操作! -
Android 8.0+: 严格限制 Manifest 静态注册隐式广播,多用动态注册。
源码分析 - Android 广播机制原理与调用链路
理解了如何使用广播后,我们深入看看 Android 系统内部是如何实现广播机制的。核心在于理解 AMS (ActivityManagerService) 的作用和 IPC (进程间通信) 。
核心原理概述
-
中心枢纽 - AMS: 广播机制的核心是
ActivityManagerService(AMS)。它运行在system_server进程,是 Android 系统的大脑,负责管理四大组件、进程生命周期以及所有广播的发送和分发。 -
注册过程 (订阅): 当一个组件(无论是静态还是动态注册)注册一个
BroadcastReceiver时,最终都需要向 AMS 报告:“我对 XXX 类型的广播感兴趣”。 -
发送过程 (发布): 当某个组件调用
sendBroadcast()时,最终会调用到 AMS。AMS 负责:- 查找所有匹配该广播 Intent 的
BroadcastReceiver(已注册的)。 - 根据广播类型(普通/有序)和优先级,决定分发的顺序和方式。
- 将广播分发给目标接收者所在的进程。
- 查找所有匹配该广播 Intent 的
-
分发过程 (执行): AMS 通过 IPC (Binder) 机制通知接收者所在进程的 AMS 客户端代理(通常是
ActivityThread中的ApplicationThread或H类) 。该进程内部再通过Handler机制,将调用BroadcastReceiver.onReceive()的任务抛到主线程的消息队列中执行。 -
异步与线程: 发送广播 (
sendBroadcast) 本身是异步的,调用后会立即返回。接收者的onReceive()一定在主线程执行。
关键源码类与调用链路
我们追踪一个典型的动态注册接收者接收普通广播的流程:
阶段 1:接收者注册 (动态注册为例)
-
ContextImpl.registerReceiver(BroadcastReceiver, IntentFilter): (在 App 进程)- 这是你在 Activity/Service 中调用的方法。
ContextImpl是Context的实际实现者。
- 这是你在 Activity/Service 中调用的方法。
-
ContextImpl.registerReceiverInternal(): (在 App 进程)- 进行一些参数处理和封装。核心是创建一个
IIntentReceiver对象 (LoadedApk.ReceiverDispatcher.InnerReceiver)。 IIntentReceiver: 这是一个 Binder 接口。它是 App 进程暴露给 AMS 的一个“钩子”(Hook)。AMS 后续需要通过这个 Binder 接口来通知 App 进程有广播到达。
- 进行一些参数处理和封装。核心是创建一个
-
ActivityManagerService.registerReceiver(): (跨进程 IPC 调用到 system_server 进程)- 通过 Binder IPC (
IActivityManager接口),App 进程将IIntentReceiver和IntentFilter等信息发送给 AMS。
- 通过 Binder IPC (
-
AMS 处理注册:
- AMS 将接收者信息(包括
IIntentReceiver这个 Binder 引用、IntentFilter、注册的进程信息等)存储在自己的内部数据结构(如mRegisteredReceivers)中。这相当于在 AMS 的“订阅清单”上添加了一条记录。
- AMS 将接收者信息(包括
阶段 2:发送广播
-
ContextImpl.sendBroadcast(Intent): (在发送者 App 进程)- 发送者调用此方法。
-
ContextImpl.sendBroadcast()->ActivityManagerService.broadcastIntent(): (跨进程 IPC 调用到 system_server 进程)- 通过 Binder IPC (
IActivityManager接口),将广播 Intent 和相关信息(如发送者权限、是否是普通广播等)发送给 AMS。
- 通过 Binder IPC (
-
AMS 处理广播 (
broadcastIntentLocked()): (在 system_server 进程)- 匹配接收者: AMS 根据收到的 Intent(主要是 Action),在自己的“订阅清单”(
mRegisteredReceivers静态注册列表、动态注册列表)中查找所有匹配该 Intent 的BroadcastReceiver。 - 处理有序广播: 如果是
sendOrderedBroadcast(),AMS 会按优先级对匹配的接收者排序,并可能设置结果接收者。 - 权限检查: 检查发送者是否有权限发送此广播,检查接收者是否有权限接收此广播(根据注册时声明的
android:permission)。 - 加入分发队列: 将满足条件的接收者(封装成
BroadcastRecord对象)加入到相应的分发队列(前台广播队列、后台广播队列)。 - 调度分发: AMS 唤醒广播分发线程 (
BroadcastQueue的工作线程) 开始处理队列中的广播。
- 匹配接收者: AMS 根据收到的 Intent(主要是 Action),在自己的“订阅清单”(
阶段 3:分发广播到接收者进程 & 执行 onReceive()
-
BroadcastQueue.processNextBroadcast(): (在 system_server 进程的广播分发线程)- 从队列中取出一个
BroadcastRecord。 - 对于该广播记录中的每一个接收者:
- 从队列中取出一个
-
BroadcastQueue.deliverToRegisteredReceiverLocked(): (在 system_server 进程)- 执行最终的权限检查(动态注册时可能没检查接收权限)。
- 关键调用:
performReceiveLocked()->app.thread.scheduleRegisteredReceiver()。 app.thread: 这是发送者注册时传递给 AMS 的那个IIntentReceiver对象(即LoadedApk.ReceiverDispatcher.InnerReceiver)所在的进程对应的IApplicationThreadBinder 代理对象。它代表了目标接收者 App 进程。
-
IApplicationThread.scheduleRegisteredReceiver(): (跨进程 IPC 调用到接收者 App 进程)- 通过 Binder IPC,AMS 通知接收者进程:“你注册的接收者(由
IIntentReceiver标识)有一个广播到了,Intent 数据是 XXX”。
- 通过 Binder IPC,AMS 通知接收者进程:“你注册的接收者(由
-
接收者进程处理 (
ApplicationThread.scheduleRegisteredReceiver()): (在接收者 App 进程的 Binder 线程池)ApplicationThread是 AMS 与 App 进程通信的主要接口实现,运行在 App 进程的 Binder 线程。- 它根据 AMS 传过来的
IIntentReceiver引用,找到对应的LoadedApk.ReceiverDispatcher对象。
-
ReceiverDispatcher.performReceive(Intent, resultCode, resultData, ...): (在接收者 App 进程的 Binder 线程)-
将接收到的 Intent 和相关信息封装成一个
Args对象。 -
关键步骤:
mActivityThread.post(args.getRunnable())mActivityThread是一个Handler(实际上是ActivityThread内部的HHandler)。args.getRunnable()返回一个Runnable,其run()方法的核心是调用receiver.onReceive(context, intent)。
-
-
Handler.post(Runnable): (在接收者 App 进程)- 这个
Runnable被发送到 App 进程的主线程消息队列。
- 这个
-
主线程
Looper处理消息:- 当主线程
Looper处理到这个Runnable消息时,执行其run()方法。
- 当主线程
-
Runnable.run()->BroadcastReceiver.onReceive(Context, Intent): (在接收者 App 进程的主线程)- 最终,你的
BroadcastReceiver子类的onReceive()方法在主线程被调用,处理广播逻辑!
- 最终,你的
静态注册流程差异
-
注册: App 安装时,系统解析
AndroidManifest.xml,将静态注册的<receiver>信息存储在PackageManagerService(PMS) 中。当系统启动或 App 安装/更新后,这些信息会被 PMS 同步给 AMS。 -
接收广播: 当匹配的隐式广播发出时,AMS 同样会找到静态注册的接收者。如果接收者所在的 App 进程尚未运行:
- AMS 会先启动该 App 进程。
- 在 App 进程启动并初始化 (
Application.onCreate()之后,ActivityThread初始化过程中),会向 AMS 报告进程启动完成。 - AMS 随后将之前收到的、匹配该 App 静态接收者的未处理广播(如果还在有效期内)发送给该进程。
- 进程启动后,后续流程与动态注册类似(AMS ->
ApplicationThread.scheduleReceiver()->ActivityThread.handleReceiver()-> 创建 Receiver 实例 ->onReceive()在主线程执行)。
-
Android 8.0+ 限制: 对于大多数隐式广播,AMS 在匹配静态注册接收者时会检查该广播类型是否在豁免列表 (白名单) 中。如果不在,则跳过这些静态注册的接收者,只分发给动态注册的接收者。
调用链路总结图 (简化版)
[发送者 App 进程]
Context.sendBroadcast(Intent)
--> (Binder IPC) -->
[system_server 进程 - AMS]
AMS.broadcastIntentLocked()
|--> 匹配注册的接收者 (静态/动态)
|--> 权限检查
|--> 将 BroadcastRecord 加入队列
|--> BroadcastQueue 处理线程 processNextBroadcast()
|--> 对每个匹配的接收者:
AMS.deliverToRegisteredReceiverLocked()
--> (Binder IPC) -->
[接收者 App 进程]
ApplicationThread.scheduleRegisteredReceiver() (在 Binder 线程)
--> 找到 ReceiverDispatcher
--> Handler.post(Runnable) (发送到主线程队列)
[接收者 App 主线程]
Runnable.run()
--> BroadcastReceiver.onReceive(Context, Intent) // 最终执行!
关键点总结 (原理与源码)
-
核心枢纽是 AMS: 所有广播的注册、发送、匹配、分发都由 AMS 在
system_server进程中集中管理。 -
IPC 无处不在:
- 注册时:App -> AMS (报告订阅信息)。
- 发送时:App -> AMS (报告广播事件)。
- 分发时:AMS -> App (通知接收者执行)。
-
IIntentReceiver是桥梁: 动态注册时,App 通过这个 Binder 对象告诉 AMS “通知我的方式”。AMS 分发时通过这个 Binder 回调 App。 -
ApplicationThread是 App 的 AMS 代理: AMS 通过调用IApplicationThread的方法(如scheduleRegisteredReceiver)来调度 App 进程内的操作。 -
onReceive()在主线程的奥秘: AMS 通过 IPC 通知到 App 进程的 Binder 线程,App 进程内部通过Handler机制将onReceive()的执行切换到主线程。 -
静态注册涉及进程启动: AMS 可能需要先启动目标 App 进程才能将广播分发给它。
-
Android 8.0+ 限制的本质: AMS 在分发隐式广播时,会过滤掉 Manifest 中静态注册且不在豁免列表中的接收者。