Android | 四大组件之 BroadcastReceiver(广播接收器)

一、工作原理

广播机制基于发布-订阅模式,核心流程如下:

  1. 发送者 :通过sendBroadcast()等API将广播(Intent)发送给 AMS(Activity Manager Service)

  2. AMS(中介):作为系统级的中枢,负责匹配Intent与已注册的接收器。

  3. 接收者 :匹配成功的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中已被标记为废弃,推荐使用LiveDataFlow替代。

四、生命周期与线程限制

  • 生命周期极短BroadcastReceiver对象的生命周期仅限于onReceive()方法的执行期间。方法一结束,系统随时可能销毁该对象以回收资源。

  • 主线程执行onReceive()默认运行在主线程(UI线程) ,因此严禁执行耗时操作 (如网络请求、数据库读写、大量计算),否则会阻塞UI,甚至引发ANR (Application Not Responding) 错误(10秒超时)。

  • 耗时任务正确姿势

    1. goAsync() :调用此方法会向系统申请延长Receiver的生命周期,允许你在另一个线程中异步处理任务。处理完毕后必须调用PendingResult.finish()通知系统。

    2. 启动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 必须动态注册。

  • 应用内通信 :优先使用LiveDataFlowEventBus等现代方案,而非BroadcastReceiver

  • 及时注销 :动态注册的Receiver必须在组件销毁前unregisterReceiver(),否则会导致内存泄漏。

七、扩展对比

方案 适用场景 优点 缺点
BroadcastReceiver 系统级广播、跨应用通信 系统级支持,可跨进程 重量级,生命周期短,版本限制多
LocalBroadcastManager 应用内通信 安全、高效 已废弃,不适用于跨进程
EventBus / RxBus 应用内解耦通信 灵活、功能强大 仅进程内,需引入第三方库
LiveData / Flow UI状态管理、数据驱动 生命周期感知、类型安全 主要面向UI层,不适合全局广播
相关推荐
爱笑鱼4 小时前
Android 系统启动机制(二):init.rc 不是普通脚本,service、action 和 property 怎样驱动启动?
android
新鲜势力呀4 小时前
深入理解 Elliot CUDA 编程核心:PHP 实现 CUDA 并行计算思想与实践
android·开发语言·php
2501_916007474 小时前
申请 iOS 推送证书并配置 APNs 群发推送教程
android·ios·小程序·https·uni-app·iphone·webview
恋猫de小郭5 小时前
Flutter 3.47 首坑,analysis_options 问题连环回归
android·前端·flutter
ue星空7 小时前
第一个安卓程序
android
淡淡的香烟7 小时前
Androidiot蓝牙配网简单封装
android·物联网
gxgldyh7 小时前
Android Framework源码解析(九):App进程诞生全流程——从AMS请求到Zygote fork源码深度拆解
android·framework·zygote·android 启动流程·android app创建流程
牛哇网络工作室14 小时前
UnityHDRP写实数字人全流程基础5—语音输入和语音识别
android·unity·c#·游戏引擎·aigc·语音识别·xcode
一笑的小酒馆14 小时前
Androidiot蓝牙配网简单封装
android