在安卓开发中,广播(Broadcast)是组件间通信的核心机制之一,它像一个“系统公告栏”,允许应用内组件、甚至跨应用间通过“发布-订阅”模式传递消息。无论是监听网络变化、接收开机完成事件,还是实现应用内模块通信,广播都扮演着重要角色。但广播的使用看似简单,实则暗藏诸多细节。
一、广播是什么、广播的作用
1. 广播的定义
广播是安卓系统提供的一种跨组件、跨进程的通信机制。其核心思想是“事件驱动”:当某个事件发生时(如网络断开、电量低、应用安装),事件源(广播发送者)会发送一条广播消息,系统中所有注册了该广播的接收者(BroadcastReceiver)都会收到这条消息,并执行相应的逻辑。
2. 广播的核心角色
广播发送者(Sender):发起广播的角色,可以是系统组件(如系统服务)、第三方应用,也可以是自身应用的Activity、Service等。发送广播的本质是通过
Intent携带消息,并调用系统API发送。广播接收者(Receiver):监听并处理广播的组件,必须继承
BroadcastReceiver类,并重写onReceive(Context, Intent)方法——这是接收广播后的核心处理逻辑入口。系统服务(AMS):Activity Manager Service,作为“中介”负责管理广播的发送与匹配。发送者将广播交给AMS,AMS根据广播的Action、权限等信息,匹配所有符合条件的接收者,再将广播分发给它们。
3. 广播的核心价值
解耦组件通信:发送者无需知道接收者的存在,接收者也无需依赖发送者,二者通过“事件”间接通信,降低组件耦合度。例如,应用的支付模块发送“支付成功”广播,订单模块、消息模块只需注册该广播即可响应,无需与支付模块直接关联。
监听系统事件:这是广播最常用的场景。系统会在特定事件发生时发送预设广播,应用通过接收这些广播实现对系统状态的感知,如监听网络变化、屏幕点亮/熄灭、电池电量等。
跨进程通信:广播天然支持跨进程传递,例如系统的“应用安装完成”广播,所有监听该事件的应用都能收到,实现跨应用的协同。
二、广播的分类
1. 按发送方式:有序广播 vs 无序广播
特性 | 无序广播(Normal Broadcast) | 有序广播(Ordered Broadcast) |
|---|---|---|
传递顺序 | 所有接收者同时接收,无固定顺序 | 按接收者优先级排序,从高到低依次传递 |
截断能力 | 无法截断,所有接收者都能收到 | 优先级高的接收者可调用 |
数据传递 | 接收者无法修改广播数据 | 优先级高的接收者可通过 |
发送API |
|
|
适用场景 | 通知类消息,如“新消息提醒”“天气更新” | 需要按顺序处理的场景,如短信拦截、权限验证 |
2. 按作用范围:全局广播 vs 本地广播
全局广播(Global Broadcast):
- 特性:广播可被系统中所有应用的接收者监听,也可发送给所有应用。
- 风险:存在安全隐患(如敏感数据通过广播泄露)和性能问题(过多应用监听可能导致广播延迟)。
- 适用场景:跨应用通信、监听系统事件。
本地广播(Local Broadcast):
- 特性:广播仅在当前应用内部传递,无法跨进程,由
LocalBroadcastManager管理。 - 优势:安全性高(避免数据泄露)、效率高(无需跨进程通信)、无ANR风险(内部使用Handler机制)。
- 适用场景:应用内组件间通信,如Activity向Service发送指令、Fragment与Activity传递消息。
3. 按发送者:系统广播 vs 自定义广播
系统广播(System Broadcast):系统预定义的广播,由系统组件发送,对应特定系统事件。例如:
网络变化:android.net.conn.CONNECTIVITY_CHANGE
开机完成:android.intent.action.BOOT_COMPLETED
电量低:android.intent.action.BATTERY_LOW
自定义广播(Custom Broadcast):开发者自行定义Action的广播,用于应用内或跨应用的自定义事件传递。例如,应用内“支付成功”广播,Action可定义为com.example.app.PAY_SUCCESS。
三、广播接收者
广播接收者(BroadcastReceiver)是处理广播的核心,其使用流程分为“定义接收者”“注册广播”“处理广播”三步,其中“注册”是关键——安卓提供两种注册方式,各有适用场景。
1. 定义广播接收者
无论哪种注册方式,接收者的定义逻辑一致:继承BroadcastReceiver,重写onReceive()方法。
// 自定义广播接收者 public class MyBroadcastReceiver extends BroadcastReceiver { // 广播接收后回调此方法 @Override public void onReceive(Context context, Intent intent) { // 1. 获取广播携带的数据 String action = intent.getAction(); String data = intent.getStringExtra("key"); // 2. 根据Action区分不同广播,执行对应逻辑 if ("com.example.app.PAY_SUCCESS".equals(action)) { // 处理支付成功逻辑:更新订单状态、提示用户等 Toast.makeText(context, "支付成功:" + data, Toast.LENGTH_SHORT).show(); } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { // 处理网络变化逻辑 checkNetworkState(context); } } // 示例:检查网络状态 private void checkNetworkState(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if (info != null && info.isConnected()) { Toast.makeText(context, "网络已连接", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "网络已断开", Toast.LENGTH_SHORT).show(); } } }注意:onReceive()方法运行在主线程(UI线程)中,不能执行耗时操作(超过10秒会触发ANR)。若需处理耗时逻辑,需启动Service或使用WorkManager,不能直接在该方法中开线程(线程可能被系统回收)。
2. 注册广播(核心)
广播接收者必须注册后才能接收广播,注册分为“静态注册”和“动态注册”,二者的核心差异在于“注册时机”和“生命周期关联”。
方式一:静态注册(AndroidManifest注册)
在AndroidManifest.xml中配置接收者,属于“全局注册”,应用未启动时也能接收广播。
<application ...> <receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.example.app.PAY_SUCCESS" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver> </application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />说明:
- enabled:控制接收者是否启用,若为false,即使注册也无法接收广播。
- exported:控制是否允许接收其他应用的广播。若为false,仅能接收自身应用发送的广播,提升安全性。
- 适用场景:适用于需要在应用未启动时接收的特殊系统广播,如开机完成、应用安装/卸载等。
- API 26后的限制:为了优化系统性能,Android8.0(API 26)后,除少数特殊系统广播外静态注册的接收者无法接收普通广播。
方式二:动态注册(代码注册)
在Activity、Service等组件的代码中通过registerReceiver()注册,广播接收者的生命周期与注册组件绑定,组件销毁前必须注销,否则会导致内存泄漏。
public class MainActivity extends AppCompatActivity { private MyBroadcastReceiver myReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1. 初始化接收者 myReceiver = new MyBroadcastReceiver(); // 2. 配置接收的广播Action IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.example.app.PAY_SUCCESS"); // 自定义广播 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_CHANGE); // 系统广播 // 若为有序广播,可设置优先级(-1000~1000,数值越大优先级越高) intentFilter.setPriority(100); // 3. 注册广播 registerReceiver(myReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); // 4. 必须注销广播,避免内存泄漏 unregisterReceiver(myReceiver); } }注册后,只有当组件(如Activity)处于运行状态时,接收者才能接收广播;组件销毁前必须调用unregisterReceiver()注销,否则系统会抛出IllegalArgumentException。但现在推荐使用 LiveData、SharedFlow(Kotlin)或 EventBus 来替代应用内的本地广播通信。
综上我们可以总结出两种注册方式的区别:
对比维度 | 静态注册 | 动态注册 |
|---|---|---|
注册时机 | 应用安装时由系统完成注册 | 组件运行时(如onCreate)手动注册 |
应用未启动时 | 可接收广播(API 26前) | 无法接收 |
生命周期 | 与应用绑定,除非卸载或禁用 | 与注册组件绑定,组件销毁需注销 |
API 26兼容性 | 大部分广播失效 | 完全兼容 |
灵活性 | 低,无法动态修改 | 高,可动态添加/移除Action |
3. 发送广播
发送广播的核心是通过Intent指定广播的Action和携带数据,再调用对应的发送API。
发送无序广播
// 1. 构建Intent,指定广播Action Intent intent = new Intent("com.example.app.PAY_SUCCESS"); // 2. 携带数据(键值对形式) intent.putExtra("orderId", "123456"); intent.putExtra("amount", 99.0); // 3. 发送无序广播 sendBroadcast(intent); // 发送时可以带上权限字符串,只有拥有该权限的接收者才能收到。 sendBroadcast(intent, "com.example.PERMISSION"); // 发送本地无序广播(仅应用内可见) LocalBroadcastManager.getInstance(this).sendBroadcast(intent);发送有序广播
Intent intent = new Intent("com.example.app.SMS_RECEIVED"); intent.putExtra("smsContent", "【验证码】123456"); // 发送有序广播,第二个参数是权限(若为null,所有应用均可接收) // 优先级高的接收者可截断广播 sendOrderedBroadcast(intent, null); // 有序广播的截断与数据修改示例(在接收者的onReceive中) @Override public void onReceive(Context context, Intent intent) { if ("com.example.app.SMS_RECEIVED".equals(intent.getAction())) { // 1. 修改广播数据 setResultData("【拦截后】验证码已屏蔽"); // 2. 截断广播,后续接收者无法收到 abortBroadcast(); } }四、实践一下:监听系统网络变化
1. 声明权限
<!-- 访问网络状态权限 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />2. 定义广播接收者
public class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 适配API 23+的网络状态获取方式 ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Network network = cm.getActiveNetwork(); NetworkCapabilities capabilities = cm.getNetworkCapabilities(network); if (capabilities != null) { if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { sendNetworkEvent(context, "WiFi网络"); } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { sendNetworkEvent(context, "移动数据网络"); } else { sendNetworkEvent(context, "无网络连接"); } } else { sendNetworkEvent(context, "无网络连接"); } } else { // 兼容低版本 NetworkInfo info = cm.getActiveNetworkInfo(); if (info != null && info.isConnected()) { if (info.getType() == ConnectivityManager.TYPE_WIFI) { sendNetworkEvent(context, "WiFi网络"); } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) { sendNetworkEvent(context, "移动数据网络"); } } else { sendNetworkEvent(context, "无网络连接"); } } } // 发送事件到Activity(可通过接口或LiveData优化) private void sendNetworkEvent(Context context, String state) { if (context instanceof MainActivity) { ((MainActivity) context).onNetworkStateChanged(state); } } }3. 动态注册与注销
public class MainActivity extends AppCompatActivity { private NetworkChangeReceiver networkReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 注册网络变化广播 networkReceiver = new NetworkChangeReceiver(); IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(networkReceiver, intentFilter); } // 接收网络状态变化事件,更新UI public void onNetworkStateChanged(String state) { TextView tvNetwork = findViewById(R.id.tv_network); tvNetwork.setText("当前网络:" + state); } @Override protected void onDestroy() { super.onDestroy(); // 注销广播 unregisterReceiver(networkReceiver); } }结语
广播(Broadcast)作为 Android 四大组件之一,虽然概念诞生已久,但它从未过时。从最早的肆意发送,到如今 Android 8.0 的隐式限制、Android 14 的导出标志强制化,广播机制的每一次演变都折射出 Android 系统对性能与安全的极致追求。
对于开发者而言,掌握广播不仅仅是学会写onReceive,更重要的是理解其背后的设计模式与系统边界。在实际开发中,我们应遵循“最小权限原则”,尽量使用动态注册,善用SharedFlow等现代技术替代本地广播,并时刻关注系统版本的迭代差异。
希望这篇文章能帮你构建起完整的广播知识体系。组件通信的方式有千万种,愿你能根据场景,选择最优雅的那一种。Happy Coding!