一、工作原理
广播机制基于发布-订阅模式,核心流程如下:
-
发送者 :通过
sendBroadcast()等API将广播(Intent)发送给 AMS(Activity Manager Service)。 -
AMS(中介):作为系统级的中枢,负责匹配Intent与已注册的接收器。
-
接收者 :匹配成功的
BroadcastReceiver会收到广播,并触发其onReceive()方法。
补充要点:
-
广播的匹配规则基于 Action(动作) 、Category(类别) 和 Data(数据) ,与
IntentFilter中声明的条件一一对应。 -
整个匹配和分发过程由系统服务(AMS)完成,对开发者透明。
二、注册方式
注册是接收广播的前提,主要有以下两种方式:
| 特性 | 静态注册 (Manifest) | 动态注册 (Code) |
|---|---|---|
| 注册位置 | AndroidManifest.xml 文件中,通过<receiver>标签声明。 |
在Java/Kotlin代码中调用registerReceiver(BroadcastReceiver, IntentFilter)。 |
| 生效时间 | 应用安装后即生效,即使应用未启动也能接收广播(前提是系统广播未受限制)。 | 仅在应用运行时生效,组件(如Activity)销毁后即失效。 |
| 典型场景 | 监听开机完成 、短信接收等需要应用在后台也能响应的系统事件。 | 监听屏幕解锁 、网络变化等仅需在应用前台处理的实时状态。 |
| 生命周期 | 由系统管理,不受应用组件生命周期影响。 | 必须手动注销 ,通常在onPause()或onDestroy()中调用unregisterReceiver(),以防内存泄漏。 |
| 安全性 | 需设置android:exported属性明确是否对外暴露。 |
默认仅在应用内部有效,相对更安全。 |
重要补充:
-
动态注册的生命周期绑定 :最佳实践是在
onResume()中注册,在onPause()中注销,确保Activity不可见时不接收无用广播。 -
Android 8.0 (API 26) 限制 :大部分隐式广播 (未指定目标包名)无法通过静态注册接收。建议使用动态注册或显式广播(
intent.setPackage())。 -
注册指定线程 :
registerReceiver()可传入Handler参数,让onReceive()在非主线程执行(但仍应避免耗时操作,因生命周期限制)。
三、广播类型
根据发送方式和特性,广播分为以下三类:
| 类型 | 发送方法 | 特点 |
|---|---|---|
| 普通广播 (Normal) | sendBroadcast() |
异步、无序 。所有匹配的接收器几乎同时收到,无法被拦截或修改,顺序不可控。 |
| 有序广播 (Ordered) | sendOrderedBroadcast() |
同步、有序 。按priority属性(-1000~1000)从高到低依次接收,高优先级可拦截 (abortBroadcast())或修改 结果数据(setResultData())。 |
| 粘性广播 (Sticky) | sendStickyBroadcast() |
已废弃 (Android 5.0)。广播会"粘"在系统上,供后续注册的接收器立即获取上一次的广播内容。不建议使用。 |
补充要点:
-
有序广播的"最终接收者" :可通过
sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, ...)指定一个resultReceiver,它会在所有普通接收器处理完毕后收到广播(即使被截断也能收到)。 -
应用内广播 :使用
LocalBroadcastManager.getInstance(context).sendBroadcast(),仅在应用内部传递,更安全、高效 。但需注意LocalBroadcastManager在AndroidX中已被标记为废弃,推荐使用LiveData或Flow替代。
四、生命周期与线程限制
-
生命周期极短 :
BroadcastReceiver对象的生命周期仅限于onReceive()方法的执行期间。方法一结束,系统随时可能销毁该对象以回收资源。 -
主线程执行 :
onReceive()默认运行在主线程(UI线程) ,因此严禁执行耗时操作 (如网络请求、数据库读写、大量计算),否则会阻塞UI,甚至引发ANR (Application Not Responding) 错误(10秒超时)。 -
耗时任务正确姿势:
-
goAsync():调用此方法会向系统申请延长Receiver的生命周期,允许你在另一个线程中异步处理任务。处理完毕后必须调用PendingResult.finish()通知系统。 -
启动
Service:通过startService()或JobService将耗时任务交给后台组件处理。
-
-
为何不能开新线程 :即使在
onReceive()中新建线程,由于Receiver生命周期极短,该线程可能尚未执行完毕就被系统回收,导致任务中断或异常。
五、安全与权限(新增重要章节)
-
android:exported属性:-
若Receiver仅供应用内部使用 ,务必设置为
false,防止外部恶意调用。 -
若需对外暴露,设置为
true并配合权限机制。
-
-
权限保护:
-
发送端 :
sendBroadcast(Intent, String)的第二个参数指定接收者必须拥有的权限。 -
接收端 :在
<receiver>标签中通过android:permission属性,限制只有持有特定权限的应用才能发送广播给它。
-
-
自定义权限 :可定义
protectionLevel="signature"的权限,确保只有同一签名的应用能收发广播。 -
显式广播 :通过
intent.setPackage(String)指定目标包名,将隐式广播变为显式,能有效防止信息泄露。
六、最佳实践与版本兼容
-
优先使用动态注册:对于大多数不需要应用在后台响应的场景,动态注册更安全、高效。
-
避免隐式广播:Android 8.0+ 对隐式广播限制严格,尽量使用显式广播或动态注册。
-
谨慎使用有序广播:因其同步特性,可能影响性能,仅在需要优先级或数据传递时使用。
-
监听网络变化 :从 Android 7.0 (API 24) 起,
CONNECTIVITY_ACTION必须动态注册。 -
应用内通信 :优先使用
LiveData、Flow或EventBus等现代方案,而非BroadcastReceiver。 -
及时注销 :动态注册的Receiver必须在组件销毁前
unregisterReceiver(),否则会导致内存泄漏。
七、扩展对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| BroadcastReceiver | 系统级广播、跨应用通信 | 系统级支持,可跨进程 | 重量级,生命周期短,版本限制多 |
| LocalBroadcastManager | 应用内通信 | 安全、高效 | 已废弃,不适用于跨进程 |
| EventBus / RxBus | 应用内解耦通信 | 灵活、功能强大 | 仅进程内,需引入第三方库 |
| LiveData / Flow | UI状态管理、数据驱动 | 生命周期感知、类型安全 | 主要面向UI层,不适合全局广播 |