当前位置: 首页 > news >正文

收藏!Android 广播(Broadcast)从注册到实战:美团大佬带你彻底搞懂组件间通信!

收藏!Android 广播(Broadcast)从注册到实战:美团大佬带你彻底搞懂组件间通信!


目录

  1. 什么是广播?
  2. 广播的分类
  3. 广播接收器实现步骤
  4. 实战:接收系统开机广播
  5. BroadcastReceiver 生命周期
  6. 广播的注册方式
  7. 常见系统广播一览
  8. 有序广播与截断
  9. 注意事项与最佳实践

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 测试方法

  1. 真机测试(推荐):

    • 安装应用
    • 重启手机
    • 查看 Logcat 过滤BootReceiver
  2. 模拟器测试

    • 模拟器开机太慢,建议用真机

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、EventBus

7. 常见系统广播一览

广播 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 测试方法

  1. 进入 App,点击「广播演示」按钮
  2. 点击「发送有序广播(会截断)」
  3. 查看 Logcat 过滤OrderedReceiver
    • 只会看到 Receiver1 的日志
    • Receiver2 不会输出(因为已被截断)
  4. 点击「发送普通广播」:
    • 两个接收器都会输出日志

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)}

总结

  1. 广播是 Android 组件间通信机制,用于发送者和多个接收者之间
  2. BroadcastReceiver是接收广播的组件
  3. 静态注册在 AndroidManifest 中,适合系统广播
  4. 动态注册在代码中,适合应用内广播
  5. onReceive() 不能做耗时操作,否则 ANR
  6. 动态注册必须取消,防止内存泄漏
  7. 开机广播是最常见的实战场景

广播虽然老旧,但在某些场景下仍然不可替代!

http://www.jsqmd.com/news/680869/

相关文章:

  • 终极指南:三步解锁QQ音乐加密文件,让音乐真正属于你
  • 3步实现窗口置顶:AlwaysOnTop让你的多任务处理效率翻倍
  • 从臃肿到轻巧:Dell G15散热控制的革命性进化之路
  • 解读马思特切削液代理商,全国口碑好的推荐有谁 - 工业设备
  • NVIDIA Profile Inspector终极指南:如何通过驱动级调优彻底解决游戏卡顿问题
  • DownKyi:3步掌握B站视频下载的终极解决方案,轻松获取8K超高清资源
  • 2026年乌兰察布好用的考研机构推荐,文与道教育线上服务如何 - 工业品牌热点
  • 如何在5分钟内完成Degrees of Lewdity中文社区本地化版的高效安装与智能配置
  • 当C#遇上Qt:一个.NET开发者的混合编程踩坑实录(附完整代码)
  • GD32F103 SPI实战:手把手教你配置主机从机全双工通信(附完整代码)
  • Rust vs C++:从‘零成本抽象’看两种语言的设计哲学与实战选择(附性能对比小实验)
  • 分析文与道考研口碑怎么样,呼和浩特地区考研辅导靠谱之选? - 工业推荐榜
  • 马思特切削液区域代理哪家口碑好,全国范围内有推荐吗? - 工业品网
  • IB课程辅导机构哪家更适合冲藤校?师资背景、HL科目覆盖与成绩提升对比 - 品牌排行榜
  • 如何快速掌握yfinance:Python金融数据采集的完整指南
  • 如何在Mac上一键解密QQ音乐加密文件:QMCDecode完整教程
  • 如何轻松管理JetBrains IDE试用期:IDE Eval Resetter完整使用指南
  • 如何导出AI对话 - DS随心转小程序
  • 终极指南:如何使用FakeLocation Xposed模块实现应用级虚拟定位
  • NVIDIA Profile Inspector终极指南:如何深度优化显卡性能的5个核心技巧
  • 拆解一台2015年的华硕A555L:聊聊‘混合硬盘’、‘低压U’和‘入门独显’那些坑
  • 2026年导轨、阀杆、双轴淬火机床厂家盘点,哪家口碑好 - 工业品牌热点
  • 2026年郑州性价比高的钢结构工程设计专业公司,费用多少钱 - mypinpai
  • 如何快速掌握SketchUp STL插件:3D打印模型转换的完整解决方案
  • 2026年3月市面上不锈钢灰厂家,精密铸造砂/碳化硅/不锈钢灰/黑碳化硅/棕刚玉/金刚砂/磨料,不锈钢灰定制哪家好 - 品牌推荐师
  • 2026年美国移民公司推荐及选择要点分析 - 品牌排行榜
  • 4个SVG设计排版技巧:文艺风教程(2026实测) - 小小智慧树~
  • 如何高效解决QQ音乐加密格式问题:macOS平台QMCDecode完整实践指南
  • 有关医疗废水预排前准备工作!
  • 2026年河南淬火机床费用探讨,价格多少合适 - 工业推荐榜