在安卓开发中,广播(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) |
|---|---|---|
| 传递顺序 | 所有接收者同时接收,无固定顺序 | 按接收者优先级排序,从高到低依次传递 |
| 截断能力 | 无法截断,所有接收者都能收到 | 优先级高的接收者可调用abortBroadcast()截断,后续接收者收不到 |
| 数据传递 | 接收者无法修改广播数据 | 优先级高的接收者可通过setResultData()修改数据,传递给后续接收者 |
| 发送API | sendBroadcast(Intent) |
sendOrderedBroadcast(Intent, String) |
| 适用场景 | 通知类消息,如"新消息提醒""天气更新" | 需要按顺序处理的场景,如短信拦截、权限验证 |
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()方法。
java
// 自定义广播接收者
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中配置接收者,属于"全局注册",应用未启动时也能接收广播。
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()注册,广播接收者的生命周期与注册组件绑定,组件销毁前必须注销,否则会导致内存泄漏。
java
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。
发送无序广播
java
// 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);
发送有序广播
java
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. 声明权限
XML
<!-- 访问网络状态权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
2. 定义广播接收者
java
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. 动态注册与注销
java
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!