收藏!Android 广播(Broadcast)从注册到实战:美团大佬带你彻底搞懂组件间通信!
收藏!Android 广播(Broadcast)从注册到实战:美团大佬带你彻底搞懂组件间通信!
目录
- 什么是广播?
- 广播的分类
- 广播接收器实现步骤
- 实战:接收系统开机广播
- BroadcastReceiver 生命周期
- 广播的注册方式
- 常见系统广播一览
- 有序广播与截断
- 注意事项与最佳实践
1. 什么是广播?
广播是 Android 中的组件间通信机制:
广播发送者 广播接收者 │ │ ├──── 发送广播(Intent)─────────────▶│ │ action="XXX" │ │ │ ▼ ▼ 系统会找到所有注册了 XXX 的接收器 │ │ │ └───────────────────────────────────┘ 多个接收器同时收到消息生活比喻
就像校园广播:
- 广播站发送通知:「明天放假」
- 所有打开收音机的同学都能收到
- 没打开收音机的同学收不到
2. 广播的分类
2.1 按发送方式分类
| 类型 | 说明 | 应用场景 |
|---|---|---|
| 普通广播 | 所有接收器同时收到 | 通用通知 |
| 有序广播 | 接收器按优先级依次收到 | 优先级处理 |
| 本地广播 | 仅应用内部接收 | 应用内通信 |
2.2 按作用范围分类
| 类型 | 说明 | 安全性 |
|---|---|---|
| 系统广播 | 系统发送的广播 | 公开 |
| 自定义广播 | 应用发送的广播 | 可控 |
3. 广播接收器实现步骤
3.1 步骤总览
1. 创建 BroadcastReceiver 类 ↓ 2. 在 AndroidManifest 注册 ↓ 3. (动态注册时)在代码中注册3.2 第一步:创建 BroadcastReceiver
classMyReceiver:BroadcastReceiver(){overridefunonReceive(context:Context,intent:Intent){// 收到广播时执行的逻辑// 注意:这个方法在主线程执行,不能做耗时操作}}3.3 第二步:注册广播接收器
方式一:静态注册(在 AndroidManifest 中)
<receiverandroid:name=".MyReceiver"android:exported="true"><intent-filter><actionandroid:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver>方式二:动态注册(在代码中)
classMainActivity:AppCompatActivity(){privatevalmyReceiver=MyReceiver()overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)// 注册广播接收器valfilter=IntentFilter().apply{addAction("com.example.MY_ACTION")}registerReceiver(myReceiver,filter)}overridefunonDestroy(){super.onDestroy()// 取消注册(必须)unregisterReceiver(myReceiver)}}4. 实战:接收系统开机广播
4.1 需求
应用安装后,每次手机开机自动启动,并在日志中输出信息。
4.2 代码实现
第一步:添加权限
<!-- AndroidManifest.xml --><uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED"/>第二步:创建广播接收器
// BootReceiver.ktclassBootReceiver:BroadcastReceiver(){companionobject{privateconstvalTAG="BootReceiver"}overridefunonReceive(context:Context,intent:Intent){// 判断是否是开机广播if(intent.action==Intent.ACTION_BOOT_COMPLETED){Log.d(TAG,"收到开机广播")Toast.makeText(context,"系统已开机,应用已启动",Toast.LENGTH_LONG).show()// 可选:启动一个 Activity// val launchIntent = Intent(context, MainActivity::class.java)// launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)// context.startActivity(launchIntent)}}}第三步:静态注册
<!-- AndroidManifest.xml --><receiverandroid:name=".BootReceiver"android:exported="true"><intent-filter><actionandroid:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver>4.3 完整 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"><!-- 开机广播权限 --><uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED"/><applicationandroid:allowBackup="true"...><!-- 开机广播接收器 --><receiverandroid:name=".BootReceiver"android:exported="true"><intent-filter><actionandroid:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver><activityandroid:name=".MainActivity".../></application></manifest>4.4 测试方法
真机测试(推荐):
- 安装应用
- 重启手机
- 查看 Logcat 过滤
BootReceiver
模拟器测试:
- 模拟器开机太慢,建议用真机
5. BroadcastReceiver 生命周期
5.1 核心方法
onReceive() 执行中 │ ├── 广播处理逻辑... │ ▼ onReceive() 返回后 │ ▼ Receiver 被系统销毁(可能被回收)5.2 重要特性
| 特性 | 说明 |
|---|---|
| 执行在主线程 | 不能做耗时操作(网络请求、IO) |
| 执行时间极短 | 通常 < 10 秒,否则 ANR |
| 生命周期极短 | onReceive 返回后可能被销毁 |
| 不能做耗时操作 | 如果需要,用 Service 或 WorkManager |
5.3 正确做法
// ❌ 错误:在 onReceive 中做耗时操作overridefunonReceive(context:Context,intent:Intent){Thread.sleep(10000)// 会 ANR!}// ✅ 正确:启动 Service 处理耗时操作overridefunonReceive(context:Context,intent:Intent){valserviceIntent=Intent(context,MyService::class.java)context.startService(serviceIntent)}6. 广播的注册方式
6.1 静态注册 vs 动态注册
| 对比 | 静态注册 | 动态注册 |
|---|---|---|
| 注册位置 | AndroidManifest | 代码 |
| 应用关闭后 | 仍然有效 | 失效 |
| 生命周期 | 应用运行期间 | 注册到取消注册之间 |
| 常用于 | 系统广播 | 应用内广播 |
6.2 动态注册示例
classMainActivity:AppCompatActivity(){privatevalscreenReceiver=object:BroadcastReceiver(){overridefunonReceive(context:Context,intent:Intent){when(intent.action){Intent.ACTION_SCREEN_OFF->{Log.d("Screen","屏幕关闭")}Intent.ACTION_SCREEN_ON->{Log.d("Screen","屏幕打开")}}}}overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)// 注册屏幕开关广播valfilter=IntentFilter().apply{addAction(Intent.ACTION_SCREEN_OFF)addAction(Intent.ACTION_SCREEN_ON)}registerReceiver(screenReceiver,filter)}overridefunonDestroy(){super.onDestroy()// 必须取消注册,防止内存泄漏unregisterReceiver(screenReceiver)}}6.3 本地广播(应用内通信)
// 使用 LocalBroadcastManager(已废弃,推荐用 LiveData 或 EventBus)// 替代方案:LiveData、Flow、EventBus7. 常见系统广播一览
| 广播 Action | 触发时机 |
|---|---|
ACTION_BOOT_COMPLETED | 系统开机完成 |
ACTION_POWER_CONNECTED | 插入电源 |
ACTION_POWER_DISCONNECTED | 拔掉电源 |
ACTION_SCREEN_OFF | 屏幕关闭 |
ACTION_SCREEN_ON | 屏幕打开 |
ACTION_BATTERY_LOW | 电量低 |
ACTION_BATTERY_OKAY | 电量恢复 |
ACTION_TIME_TICK | 每分钟一次 |
ACTION_DATE_CHANGED | 日期改变 |
ACTION_LOCALE_CHANGED | 系统语言改变 |
ACTION_CONFIGURATION_CHANGED | 配置改变(屏幕旋转等) |
ACTION_PACKAGE_ADDED | 安装了新应用 |
ACTION_PACKAGE_REMOVED | 卸载了应用 |
8. 有序广播与截断
8.1 什么是有序广播?
有序广播会按优先级顺序依次传递给接收器,每个接收器都有机会处理并决定是否截断。
发送有序广播 ↓ Receiver1(优先级 100)收到 → 可以截断 ↓ Receiver2(优先级 50)← 被截断则收不到 ↓ Receiver3(优先级 0)← 被截断则收不到8.2 普通广播 vs 有序广播
| 对比 | 普通广播 | 有序广播 |
|---|---|---|
| 发送方式 | sendBroadcast() | sendOrderedBroadcast() |
| 接收顺序 | 所有接收器同时收到 | 按优先级顺序依次收到 |
| 截断能力 | 不能截断 | 可以调用abortBroadcast()截断 |
| 结果传递 | 无 | 可以传递结果数据 |
8.3 有序广播发送与接收
发送有序广播:
valintent=Intent("com.example.ORDERED_BROADCAST").apply{putExtra("message","这是一条有序广播")}sendOrderedBroadcast(intent,null)接收器注册(指定优先级):
<!-- 高优先级接收器(会截断)--><receiverandroid:name=".OrderedReceiver1"android:exported="true"><intent-filterandroid:priority="100"><actionandroid:name="com.example.ORDERED_BROADCAST"/></intent-filter></receiver><!-- 低优先级接收器(可能被截断收不到)--><receiverandroid:name=".OrderedReceiver2"android:exported="true"><intent-filterandroid:priority="50"><actionandroid:name="com.example.ORDERED_BROADCAST"/></intent-filter></receiver>8.4 截断广播
接收器可以调用abortBroadcast()截断有序广播,后续接收器将不会收到:
classOrderedReceiver1:BroadcastReceiver(){overridefunonReceive(context:Context,intent:Intent){Log.d(TAG,"Receiver1 收到广播")valmessage=intent.getStringExtra("message")Log.d(TAG,"收到消息:$message")// 截断广播 - 后续接收器将不会收到abortBroadcast()Log.d(TAG,"Receiver1 已截断广播")}}8.5 有序广播截断流程图
发送有序广播(message="测试") │ ▼ OrderedReceiver1(优先级100)收到 │ Log: "Receiver1 收到广播" │ Log: "收到消息: 测试" │ abortBroadcast() 截断! │ ▼ OrderedReceiver2(优先级50) │ ← 不会收到!被截断了 │ ▼ OrderedReceiver3(优先级0) │ ← 不会收到!被截断了8.6 完整实战代码
BroadcastSenderActivity.kt(发送者):
classBroadcastSenderActivity:AppCompatActivity(){companionobject{constvalACTION="com.example.ORDERED_BROADCAST"}privatefunsendOrderedBroadcast(){valintent=Intent(ACTION).apply{putExtra("message","这是一条有序广播消息")}sendOrderedBroadcast(intent,null)Toast.makeText(this,"已发送有序广播",Toast.LENGTH_LONG).show()}privatefunsendNormalBroadcast(){valintent=Intent(ACTION).apply{putExtra("message","这是一条普通广播消息")}sendBroadcast(intent)Toast.makeText(this,"已发送普通广播",Toast.LENGTH_LONG).show()}}OrderedReceiver1.kt(高优先级,截断):
classOrderedReceiver1:BroadcastReceiver(){companionobject{constvalTAG="OrderedReceiver1"constvalACTION="com.example.ORDERED_BROADCAST"}overridefunonReceive(context:Context,intent:Intent){Log.d(TAG,"=== Receiver1 收到广播 ===")Log.d(TAG,"Action:${intent.action}")Log.d(TAG,"当前优先级: 100")valmessage=intent.getStringExtra("message")Log.d(TAG,"收到消息:$message")// 截断广播 - 后续接收器将不会收到abortBroadcast()Log.d(TAG,"Receiver1 已截断广播,Receiver2 不会收到")}}OrderedReceiver2.kt(低优先级):
classOrderedReceiver2:BroadcastReceiver(){companionobject{constvalTAG="OrderedReceiver2"}overridefunonReceive(context:Context,intent:Intent){Log.d(TAG,"=== Receiver2 收到广播 ===")Log.d(TAG,"当前优先级: 50")valmessage=intent.getStringExtra("message")Log.d(TAG,"收到消息:$message")Log.d(TAG,"Receiver2 处理完成")}}8.7 测试方法
- 进入 App,点击「广播演示」按钮
- 点击「发送有序广播(会截断)」
- 查看 Logcat 过滤
OrderedReceiver:- 只会看到 Receiver1 的日志
- Receiver2 不会输出(因为已被截断)
- 点击「发送普通广播」:
- 两个接收器都会输出日志
8.8 优先级范围
| 优先级 | 范围 | 说明 |
|---|---|---|
| 最高 | +1000 ~ +2147483647 | 系统保留 |
| 高 | +100 ~ +999 | 自定义高优先级 |
| 中 | -100 ~ +99 | 默认优先级 |
| 低 | -1000 ~ -101 | 自定义低优先级 |
优先级相同的时候,按注册顺序传递。
9. 注意事项与最佳实践
9.1 安全问题
<!-- 静态注册的接收器,默认 exported=true,任何应用都能发送广播给它 --><!-- 如果只用于应用内部,应该设为 exported=false --><receiverandroid:name=".MyReceiver"android:exported="false"><!-- 应用内使用,不暴露给外部 --></receiver><!-- 如果需要接收外部广播,必须加权限 --><receiverandroid:name=".MyReceiver"android:exported="true"android:permission="android.permission.BLUETOOTH"><!-- 只有持有 BLUETOOTH 权限的应用才能发送 --></receiver>9.2 广播权限
// 发送广播时指定权限sendBroadcast(intent,"com.example.MY_PERMISSION")// 接收广播时要求权限registerReceiver(receiver,filter,"com.example.MY_PERMISSION",null)9.3 ANR 问题
BroadcastReceiver 的 onReceive() 必须快速执行:
onReceive() 执行时间过长(>10秒)→ ANR(应用无响应)解决方案:
- 将耗时操作放到 Service 或 WorkManager 中执行
- 使用 goAsync() 允许后台执行(但仍需尽快完成)
overridefunonReceive(context:Context,intent:Intent){valpendingResult=goAsync()// 延长生命周期Thread{// 在后台线程执行耗时操作doSomething()pendingResult.finish()// 完成后必须调用}.start()}9.4 内存泄漏注意
动态注册必须在 onDestroy 中取消:
overridefunonDestroy(){super.onDestroy()// 动态注册的广播必须取消注册unregisterReceiver(screenReceiver)}总结
- 广播是 Android 组件间通信机制,用于发送者和多个接收者之间
- BroadcastReceiver是接收广播的组件
- 静态注册在 AndroidManifest 中,适合系统广播
- 动态注册在代码中,适合应用内广播
- onReceive() 不能做耗时操作,否则 ANR
- 动态注册必须取消,防止内存泄漏
- 开机广播是最常见的实战场景
广播虽然老旧,但在某些场景下仍然不可替代!
