引言
什么是广播
初次听到"广播"这个词时,可能会觉得有些抽象。但如果联想到 IP 协议中的广播机制,或者现实生活中的广播系统------即向特定范围内所有接收者发送一条统一信息的方式,就不难理解其核心含义了。
在 Android 中,广播机制正是基于这一思想构建的:它是一种基于事件驱动的通信方式,允许系统或应用程序发送一条消息(称为"广播"),通知其他组件某些事件的发生。这些事件可以来源于用户的操作、系统状态的变化(如网络连接、电量变化等),也可以是应用内部的业务逻辑触发。
通过注册广播接收器(BroadcastReceiver),应用组件可以在不直接耦合的前提下监听并响应这些事件,从而实现松耦合、高内聚的通信结构。这种机制为组件间协作、跨应用通信以及系统级事件感知提供了强大的支持。
广播能做什么
当系统状态发生改变时,Android 系统会主动发送一系列广播(Broadcast),用于通知应用当前设备环境的变化。通过注册相应的 BroadcastReceiver,应用可以监听这些广播并作出响应,从而实现对系统事件的感知和行为调整。
获取系统状态
以下是几个常见的系统广播示例:
- 设备启动完成。当系统完成启动后,会发出
ACTION_BOOT_COMPLETED
广播。应用可以通过监听该广播,在设备重启后执行初始化操作或启动后台服务。
⚠️ 注意:需要声明权限:
Xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-
网络连接状态变化。通过监听
CONNECTIVITY_ACTION
广播,应用可以实时感知网络状态的变化,例如 Wi-Fi 连接、移动数据切换等。这为在网络状态变更时进行数据同步或提示用户提供了基础支持。 -
电池电量变化。Android 提供了多个与电池状态相关的广播,如
ACTION_BATTERY_LOW
(电量低)和ACTION_BATTERY_OKAY
(电量恢复)。应用可以根据当前电量情况动态调整资源使用策略,例如降低刷新频率或提示用户充电。 -
时间变化事件。系统还提供了一些与时间相关的广播,例如每分钟触发一次的 TIME_TICK。开发者可以利用此类广播实现定时提醒、日历更新、按时间展示不同内容等功能。
跨应用通信
应用之间可以通过广播机制实现跨进程通信。通过发送一条特定的广播消息,一个应用可以将信息传递给另一个应用;只要目标应用对该广播的 ACTION 进行了监听并注册了相应的 BroadcastReceiver,就可以接收到该广播并作出响应。这种方式为跨应用通信提供了一种轻量级且灵活的解决方案。
不过,使用广播进行跨应用通信的前提是:双方应用必须事先约定相同的广播 Action,并且在必要时声明和授予相应的自定义权限,以确保通信的安全性和可控性。
应用内通信
Android 中的广播机制不仅可用于跨应用通信,同样也适用于应用内部不同组件之间的通信 。通过在应用内发送和接收广播,组件之间可以实现松耦合 的交互方式,无需彼此持有引用 即可完成事件通知与数据传递。这种方式特别适用于需要多个组件监听同一事件、全局状态变更通知等场景=。
不推荐发送大量数据与高频通信
使用广播
在 Android 中,广播的使用主要涉及到两个方面:发送广播和接收广播 。接收广播可以通过两种方式来注册广播接收器(BroadcastReceiver):动态注册和静态注册 。发送广播则分为:发送经典广播与有序广播
接收广播
动态注册监听
动态注册是指在程序运行过程中,通过代码手动注册广播接收器的方式 。这种方式具有更高的灵活性,且从 Android 8.0(API 26)
开始,系统对隐式广播的静态注册进行了限制,因此动态注册成为当前推荐的主要方式。
第一步:需要实现一个广播接收器 ,继承BroadReceiver
,实现onReceive()
java
public class TimeChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "时间发生变化", Toast.LENGTH_SHORT).show();
}
}
onReceive()
方法会在接收到匹配的广播时被调用,注意不要在此执行耗时操作,否则可能导致 ANR。
第二步:在 Activity
或 Service
中注册广播接收器
java
binding.buttonRegisterReceiver.setOnClickListener(v -> {
IntentFilter filter = new IntentFilter();
// 需要过滤器过滤
filter.addAction("android.intent.action.TIME_TICK");
timeChangeReceiver = new TimeChangeReceiver();
// 当注册的非系统广播时,需要填写是否允许外部应用触发
// registerReceiver(myTimeBroadcast, filter, RECEIVER_NOT_EXPORTED);
registerReceiver(timeChangeReceiver, filter);
});
之后,每过一分钟,应用就会收到一个消息,使用timeChangeReceiver.onReceive(...)
接收
需要注意的是,动态注册的广播接收器必须在不再需要时手动注销 ,以避免内存泄漏。通常建议在组件销毁前(如
onDestroy()
)调用unregisterReceiver()
静态注册监听
动态注册通过编写代码实现,具有高度的灵活性,尤其适用于运行时根据需求动态控制广播接收器的场景。然而,它也存在一个根本性的限制:只有在应用启动并执行注册操作后,才能开始监听广播。这意味着如果应用未处于活跃状态,将无法接收到相关事件。
与之相对,静态注册不依赖于应用是否启动 ,即使应用当前未运行,只要系统发出匹配的广播,接收器仍会被激活并处理该广播。这使得静态注册非常适合用于监听系统级事件(如设备启动完成、电池状态变化等),在这些场景下,要求应用在特定系统事件发生时能够被自动唤醒并作出响应。
第一步:AndroidManifest.xml
注册reveiver
xml
<receiver
android:name=".broadcast.MyStaticReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
第二步:AndroidManifest.xml
申请权限 ( 有时需要 )
xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
第三步:编写对应接收器
java
public class MyStaticReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "设备启动完成", Toast.LENGTH_LONG).show();
}
}
onReceive()
方法会在接收到匹配的广播时被调用,注意不要在此执行耗时操作,否则可能导致 ANR。
发送
在 Android 中,除了接收广播,应用也可以主动发送广播,以通知其他组件或应用某个事件的发生。根据广播的传递方式和特性,Android 提供了多种广播类型。如下两种
标准广播
标准广播是一种异步广播,系统会将广播同时发送给所有匹配的 BroadcastReceiver。各个接收器之间是并行接收的,彼此之间无法相互影响,也没有优先级之分。
- 先注册接收器用于测试
java
binding.registerButton.setOnClickListener(view -> {
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.hellocast.SEND");
// 允许外部触发
registerReceiver(myBroadcast, filter, RECEIVER_EXPORTED);
Toast.makeText(this, "MyBroadcast", Toast.LENGTH_SHORT).show();
});
- 发送广播
java
binding.sendButton.setOnClickListener(view -> {
Intent intent = new Intent("com.example.hellocast.SEND");
// 设置指定目标包名,可将隐形广播变为显性广播
// intent.setPackage(getPackageName());
intent.putExtra("message", "这是一条标准广播");
sendBroadcast(intent);
});
- 结果
text
com.example.hellocast D onReceive: com.example.hellocast, 这是一条标准广播
上面有许多注意的点
- 静态注册的 BroadcastReceiver 是无法接收隐式广播的,除非携带包名,使得隐式广播变显式广播。(可以自己写代码尝试一下,会发现如果不携带包名,只有动态注册的接收器可以接收广播)
- 动态注册接收器无此限制,但当 registerReceiver 的第三个参数为 RECEIVER_NOT_EXPORTED 时,也必须要携带包名才能触发
有序广播
经典广播的广播是并行 的,只要符合要求的接收者都能接收。而有序广播是串行的,必须前接收器允许向后传输,后接收器才能接收到。
发送广播
只需要将sendBroadcast
改为sendOrderedBroadcast
。第二个参数为所要求的权限。
java
binding.sendButton.setOnClickListener(view -> {
Intent intent = new Intent("com.example.hellocast.SEND");
// 转为显式
intent.setPackage(getPackageName());
intent.putExtra("message", "这是一条标准广播");
sendOrderedBroadcast(intent, null);
});
接收广播
实际上广播接收器不进行修改就可以使用。不过这就体现不出有序广播的特点(按优先级广播 )了。如果需要设置优先级,可以使用filter.setPriority(100);
或<intent-filter android:priority="1000">
。例如 动态注册
java
binding.registerButton.setOnClickListener(view -> {
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.hellocast.SEND");
filter.setPriority(100); // 设置优先级
registerReceiver(myBroadcast, filter, RECEIVER_EXPORTED);
Toast.makeText(this, "MyBroadcast", Toast.LENGTH_SHORT).show();
});
静态注册
java
<receiver
android:name=".broadcast.MyReceiver2"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="com.example.hellocast.SEND" />
</intent-filter>
</receiver>
如果需要中止有序广播的向后传播,可以使用abortBroadcast();
来中止
java
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive: com.example.hellocast.SEND");
abortBroadcast();
}
广播的权限机制
在安卓系统中,曾经出现过许多"恶意软件"利用广播机制来"作恶"的现象,某些"恶意软件"可能利用广播机制来监听敏感信息、启动未经授权的服务或执行其他形式的攻击。为了防止这种情况的发生,Android 提供了严格的权限机制来控制广播的发送和接收。
系统广播
在系统广播中,由于部分系统广播往往涉及到重要信息,故大部分情况下,接收系统广播都需要申请权限 。例如,要接收设备启动完成后的广播 (ACTION_BOOT_COMPLETED
),你需要在 AndroidManifest.xml
文件中添加以下权限声明:
xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
具体可以查阅developer.android.google.cn/reference/a...
自定义广播
除了接收系统广播外,开发者还可以创建自己的广播来实现应用内部或跨应用之间的通信。为了确保这些广播的安全性,可以通过设置权限来限制哪些应用可以发送或接收它们。
发送广播时指定权限
当你发送一个自定义广播时,可以通过 sendBroadcast(intent, receiverPermission)
方法来限制接收者的权限。例如:
Java
Intent intent = new Intent("com.example.CUSTOM_ACTION");
intent.putExtra("message", "这是一个受保护的广播");
// 只有拥有 com.example.permission.RECEIVE_CUSTOM_BROADCAST 权限的应用才能接收
sendBroadcast(intent, "com.example.permission.RECEIVE_CUSTOM_BROADCAST");
接收广播时声明权限
为了让应用能够接收带有权限要求的广播,你必须在 AndroidManifest.xml 文件中声明所需的权限:
xml
<uses-permission android:name="com.example.permission.RECEIVE_CUSTOM_BROADCAST"/>
并且,在注册广播接收器时,可以在 标签中指定权限:
xml
<receiver android:name=".MyCustomReceiver"
android:permission="com.example.permission.RECEIVE_CUSTOM_BROADCAST">
<intent-filter>
<action android:name="com.example.CUSTOM_ACTION" />
</intent-filter>
</receiver>
附录
项目 | 动态注册 | 静态注册 |
---|---|---|
注册位置 | Java/Kotlin 代码中 | AndroidManifest.xml |
是否需要应用启动 | 是 | 否 |
生命周期控制 | 手动管理(注册/注销) | 系统自动激活 |
兼容性 | 支持所有广播类型 | 部分隐式广播受限于 Android 8.0+ |
推荐用途 | 运行时监听、组件间通信 | 监听系统事件、跨应用广播 |
安全性 | 更高(生命周期可控) | 稍低(可能被其他应用触发) |
组合方式 | 说明 | 推荐程度 |
---|---|---|
显式广播 + 动态注册 | 安全、灵活,推荐用于应用内部通信 | ✅ 强烈推荐 |
隐式广播 + 动态注册 | 安全且兼容性好,是 Android 8.0+ 推荐方式 | ✅ 强烈推荐 |
显式广播 + 静态注册 | 不常见,显式广播一般不需要静态注册 | ⚠️ 少数情况可用 |
隐式广播 + 静态注册 | 从 Android 8.0 开始限制较多,仅几个系统广播可用,建议避免 | ❌ 不推荐 |
系统广播
安卓系统广播列表:<Android SDK>/platforms/<任意android api版本>/data/broadcast_actions.txt
在Android 8.0系统之后,所有隐式广播都不允许使用静态注册的方式来接收了 。隐式广播指的是那些没有具体指定发送给哪个应用程序的广播,大多数系统广播属于隐式广播,但是少数特殊的系统广播目前仍然允许使用静态注册的方式来接收 。这些特殊的系统广播列表详见developer.android.google.cn/develop/bac...