在 Android 开发中,广播接收器(Broadcast Receiver)是一个非常重要的组件,它能帮助应用接收来自系统或其他应用的事件通知,实现跨组件、跨应用的通信。大家可以把广播接收器想象成一个"收音机"。它的作用是监听系统或应用发出的"广播消息",并在收到消息后执行相应的操作。
(一)基础概念
BroadcastReceiver用于监听系统或应用发出的广播事件,实现跨组件通信。其特点是发送方无需关注接收方是否存在或如何处理数据。
一、广播的类型
- 系统广播:由系统发出,比如电池电量低、网络状态变化、屏幕开关等
ACTION_BATTERY_LOW:电池电量低。
ACTION_BOOT_COMPLETED:设备启动完成。
- 自定义广播:由应用发出,用于应用内部或应用之间的通信。
在App中定义一个广播,比如"任务完成",然后在其他地方接收并处理。
二、生命周期管理
广播接收器的生命周期非常短暂,它只存在于接收到广播并处理完广播的这段时间内。
- **接收广播:**当广播发出时,系统会创建广播接收器的实例,并调用它的onReceive()方法。
- **完成任务:**onReceive()方法执行完毕后,广播接收器的实例就会被销毁。
三、典型场景
|--------|-------------------------------------------|
| 典型场景 | 实现方案 |
| 强制下线功能 | 发送全局广播通知所有页面退出登录,动态注册接收器处理跳转逻辑 |
| 多应用协同 | 通过自定义广播与权限控制,实现应用间数据传递与功能触发 |
| 系统事件响应 | 静态注册监听SCREEN_ON/SCREEN_OFF等系统广播,实现锁屏/亮屏逻辑 |
(二)注册方式
一、静态注册(Manifest 注册)
静态注册是指在 AndroidManifest.xml 中声明广播接收器,即使应用未启动,系统也能向其发送广播。
1. 核心特点
- 独立于应用生命周期:即使应用进程被杀或未启动,仍能接收广播。
- 系统事件响应:常用于监听系统级事件(如开机完成、网络变化、电量低等)。
- 局限性:Android 8.0(API 26)后,大部分隐式广播(不指定接收者的广播)被限制,需改用动态注册或显式广播。
2. 实现步骤
1. 创建广播接收器类 :继承 BroadcastReceiver
,重写 onReceive()
方法。
java
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 处理接收到的广播(如启动服务、显示通知)
Toast.makeText(context, "系统启动完成!", Toast.LENGTH_SHORT).show();
}
}
2. 在 AndroidManifest.xml 中注册:
XML
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true"> <!-- 允许接收外部应用的广播 -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- 声明接收开机广播的权限 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
3. 典型应用场景
- 监听系统事件 :如
BOOT_COMPLETED
(开机完成)、CONNECTIVITY_CHANGE
(网络变化)。- 跨应用通信:接收其他应用发送的广播(需确保广播为显式或已注册的隐式广播)。
4. 注意事项
- Android 8.0+ 限制 :大部分隐式广播无法通过静态注册接收(如
ACTION_PACKAGE_REPLACED
),需改用动态注册。- 性能开销:静态注册的接收器常驻内存,可能影响应用启动速度,建议仅用于必要场景。
- 权限要求 :某些系统广播需要额外权限(如
RECEIVE_BOOT_COMPLETED
)。
二、动态注册(代码注册)
动态注册是指在 Activity、Service 或其他组件 中通过代码注册广播接收器,生命周期与注册的组件绑定。
1. 核心特点
- 生命周期绑定 :随组件的创建而注册,销毁而注销(如 Activity 的
onCreate()
注册,onDestroy()
注销)。- 灵活性高:适用于仅在特定界面或时间段内需要监听的广播(如页面内的事件通知)。
- 响应及时:能更快响应应用内部的自定义广播。
2. 实现步骤
- 创建广播接收器实例:可匿名内部类或独立类。
- 注册接收器 :在组件生命周期的合适时机(如
onCreate()
)调用registerReceiver()
。- 注销接收器 :在组件销毁前(如
onDestroy()
)调用unregisterReceiver()
,避免内存泄漏。
java
public class MainActivity extends AppCompatActivity {
private BroadcastReceiver networkReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建意图过滤器,指定接收的广播类型
IntentFilter filter = new IntentFilter();
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
// 创建广播接收器实例(匿名内部类)
networkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 处理网络变化事件
checkNetworkStatus();
}
};
// 注册广播接收器
registerReceiver(networkReceiver, filter);
}
private void checkNetworkStatus() {
// 检查网络状态的逻辑
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销广播接收器,避免内存泄漏
unregisterReceiver(networkReceiver);
}
}
3. 典型应用场景
- Activity 内事件监听:如监听蓝牙状态变化、下载完成通知。
- 应用内部通信:自定义广播(如登录状态变更、数据刷新)。
- 敏感权限广播 :如 Android 8.0+ 的
CONNECTIVITY_CHANGE
只能通过动态注册接收。4. 注意事项
- 必须手动注销 :忘记在
onDestroy()
中注销会导致内存泄漏(接收器持有 Activity 引用)。- 组件销毁后失效:如 Activity 退出后,动态注册的接收器无法继续接收广播。
三. 静态 vs 动态注册对比
|-----------------|-------------------------------------|-------------------------|
| 特性 | 静态注册(Manifest) | 动态注册(代码) |
| 生命周期 | 独立于应用,系统启动即生效 | 依赖与注册组件(如Activity)的生命周期 |
| 响应时间 | 应用未启动时也能接收 | 组件创建后才能接收 |
| 适用场景 | 系统事件(如开机、网络变化) | 组件内事件(如页面数据刷新,蓝牙状态) |
| Android 8.0+ 限制 | 大部分隐式广播失效 | 无限制 |
| 内存占用 | 常驻内存,可能增加应用启动开销 | 组件销毁后自动释放,更轻量 |
| 注册位置 | AndroidManifest.xml | Activity / Service 代码中 |
| 权限要求 | 部分广播需要额外权限(如RECEIVE_BOOT_COMPLETED) | 通常无需特殊权限 |
四. 如何选择
- 优先使用动态注册:除非必须在应用未启动时接收广播(如开机启动),否则尽量用动态注册。
- 避免耗时操作 :
onReceive()
在主线程执行,避免耗时操作(如网络请求),否则可能触发 ANR。- 及时注销动态接收器 :确保在组件销毁前调用
unregisterReceiver()
。- Android 8.0+ 适配:对于系统广播,优先用动态注册;发送广播时尽量用显式广播(指定包名或组件名)。
掌握静态和动态注册的区别,能让你更合理地使用广播接收器,优化应用性能并避免常见问题。
(三)无序广播和有续广播
在 Android 中,广播分为有序广播(Ordered Broadcast)和无序广播(Normal Broadcast) ,它们的核心区别在于发送方式 、接收顺序 和数据传递机制。以下是详细对比和使用场景分析:
一、无序广播(Normal Broadcast)
1. 核心特点
- 完全异步 :广播发出后,所有接收器同时并行接收,没有固定顺序。
- 不可拦截:接收器无法阻止广播继续传递,也不能修改广播携带的数据。
- 效率高:适用于不需要优先级处理的场景,系统开销小。
2. 发送方式
通过 Context.sendBroadcast(Intent)
发送:
java
// 发送无序广播
Intent intent = new Intent("com.example.MY_ACTION");
sendBroadcast(intent);
3. 典型应用场景
- 系统事件通知 :如网络连接变化(
CONNECTIVITY_CHANGE
)、电池电量变化(BATTERY_CHANGED
)。- 应用内部通知:如刷新 UI、数据加载完成等。
4. 示例代码
java
// 发送方
Intent intent = new Intent("com.example.UPDATE_UI");
sendBroadcast(intent);
// 接收方(多个接收器同时接收)
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 更新 UI 或执行其他操作
}
}
二、有序广播(Ordered Broadcast)
1. 核心特点
- 同步执行 :广播按接收器的优先级顺序依次接收(优先级高的先接收)。
- 可拦截可修改 :
- 高优先级接收器可通过
abortBroadcast()
拦截广播,阻止后续接收器接收。 - 可通过
setResultData()
或setResultExtras()
修改广播携带的数据,供后续接收器使用。
- 高优先级接收器可通过
- 数据传递 :通过
getResultData()
和getResultExtras()
获取前一个接收器修改后的数据。
2. 发送方式
通过 Context.sendOrderedBroadcast(Intent, receiverPermission)
发送:
java
// 发送有序广播(第二个参数为权限,null 表示无需权限)
Intent intent = new Intent("com.example.ORDERED_ACTION");
sendOrderedBroadcast(intent, null);
3. 优先级设置
在 AndroidManifest.xml
或代码中通过 intent-filter
的 android:priority
属性设置优先级(值越大优先级越高,范围 -1000
到 1000
):
XML
<receiver android:name=".HighPriorityReceiver">
<intent-filter android:priority="1000">
<action android:name="com.example.ORDERED_ACTION" />
</intent-filter>
</receiver>
<receiver android:name=".LowPriorityReceiver">
<intent-filter android:priority="500">
<action android:name="com.example.ORDERED_ACTION" />
</intent-filter>
</receiver>
4. 拦截与数据修改
高优先级接收器可拦截广播或修改数据:
java
// 高优先级接收器(Priority 1000)
public class HighPriorityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 修改广播数据
setResultData("Modified by HighPriorityReceiver");
// 可选:拦截广播,阻止低优先级接收器接收
// abortBroadcast();
}
}
// 低优先级接收器(Priority 500)
public class LowPriorityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 获取高优先级接收器修改后的数据
String data = getResultData();
Log.d("Receiver", "Received data: " + data);
}
}
5. 典型应用场景
- 权限控制:如系统电量低时,优先让核心应用处理(如保存数据),再通知其他应用。
- 分层处理:如广告过滤(高优先级接收器拦截广告广播,低优先级接收器接收不到)。
- 数据聚合:多个接收器按顺序处理同一条广播数据(如添加不同的响应头)。
三、有序 vs 无序广播对比
|----------|-----------------------|------------------------------------------|
| 特性 | 无序广播(Noraml) | 有序广播(Ordered) |
| 发送方式 | sendBroadcast(intent) | sendOrderedBroadcast(intent,permission) |
| 接收顺序 | 无顺序,并行接收 | 按优先级顺序依次接收 |
| 拦截能力 | 不可拦截 | 可通过abortBroadcast()拦截 |
| 数据修改 | 不可修改 | 可通过setResultData()或setResultExtras()修改数据 |
| 性能开销 | 低(异步同行) | 高(同步串行) |
| 使用场景 | 系统通知 | 权限控制、分层处理、数据聚合 |
四、注意事项
- 优先级冲突:若多个接收器优先级相同,接收顺序可能不确定(与注册时间或系统调度有关)。
- ANR 风险:有序广播在主线程同步执行,若某个接收器耗时过长,可能触发 ANR。
- 权限限制 :发送有序广播时,第二个参数可指定接收者需要的权限(如
android.permission.RECEIVE_SMS
)。五、如何选择
- 优先使用无序广播:除非需要优先级控制或数据传递,否则尽量用无序广播提升性能。
- 避免长时间处理:有序广播的接收器应快速处理,避免阻塞后续接收器。
- 明确终止点 :若需拦截广播,确保在合适的优先级接收器中调用
abortBroadcast()
。
(四)全局广播和本地广播
在 Android 中,广播(Broadcast)分为本地广播(LocalBroadcastManager)和全局广播(普通广播) ,它们的核心区别在于作用域 、安全性 和性能。以下是详细对比和使用场景分析:
一、全局广播
1. 核心特点
- 跨应用通信:广播可被系统或其他应用接收 / 发送,作用域为整个系统。
- 系统事件支持:可监听系统级事件(如开机完成、网络变化)。
- 安全风险:公开广播可能被恶意应用拦截或注入数据,需谨慎处理敏感信息。
- 性能开销:发送全局广播涉及跨进程通信,开销较大。
2. 实现方式
通过 Context.sendBroadcast()
或 sendOrderedBroadcast()
发送,通过 registerReceiver()
或 AndroidManifest.xml
注册接收器。
java
// 发送全局广播
Intent intent = new Intent("com.example.GLOBAL_ACTION");
sendBroadcast(intent);
// 注册全局接收器(动态)
registerReceiver(receiver, new IntentFilter("com.example.GLOBAL_ACTION"));
3. 典型应用场景
- 系统事件监听 :如
BOOT_COMPLETED
、CONNECTIVITY_CHANGE
。- 跨应用交互:如应用间共享数据、通知其他应用执行操作。
- 组件间通信:同一应用内不同组件(Activity、Service)间的通信。
4. 安全隐患
- 隐式广播风险:不指定接收者的广播可能被恶意应用拦截(如获取敏感数据)。
- 权限缺失:发送敏感广播时未声明权限,可能导致数据泄露。
二、本地广播
1. 核心特点
- 应用内通信:广播仅在应用内部传播,其他应用无法接收或发送,安全性高。
- 高效轻量:不涉及跨进程通信,性能优于全局广播。
- 生命周期管理:接收器随注册的组件销毁自动失效,无需手动注销(但建议主动注销)。
- 不支持静态注册:只能通过代码动态注册。
2. 实现方式
通过 LocalBroadcastManager
类发送和接收广播:
java
// 注册本地接收器
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 处理广播
}
};
manager.registerReceiver(receiver, new IntentFilter("com.example.LOCAL_ACTION"));
// 发送本地广播
Intent intent = new Intent("com.example.LOCAL_ACTION");
manager.sendBroadcast(intent);
// 注销接收器(建议在 onDestroy() 中调用)
manager.unregisterReceiver(receiver);
3. 典型应用场景
- 应用内部组件通信:如 Activity 与 Service 之间、Fragment 与 Activity 之间。
- 敏感数据传递:如用户令牌、隐私信息,避免泄露到外部。
- 频繁通信:如实时数据更新(如聊天消息、位置变化),减少全局广播的性能开销。
4. 优势
- 安全性:数据仅在应用内可见,避免被其他应用拦截。
- 性能优化:无需跨进程序列化和反序列化数据,执行速度快。
- 内存管理:组件销毁时,若未手动注销接收器,系统会自动清理,降低内存泄漏风险。
三、本地 vs 全局广播对比
|------------|---------------------------------------|-------------------------|
| 特性 | 本地广播(LocalBroadcastManager) | 全局广播(普通广播) |
| 作用域 | 仅应用内部可见 | 整个系统可见(跨应用) |
| 安全性 | 高(数据不泄密) | 低(需注意权限和隐式广播风险) |
| 性能 | 高效(不涉及跨进程) | 开销大(跨进程通信) |
| 静态注册支持 | 不支持(只能支持动态注册) | 支持(AndroidManifest.xml) |
| 系统事件监听 | 不支持(无法接收系统广播) | 支持(如BOOT_COMPLETED) |
| 生命周期管理 | 组件销毁后自动生效(建议手动注销) | 需手动注销,否则可能存在内存泄漏 |
| 发送方式 | LocalBroadcastManager.sendBroadcast() | Context.sendBroadcast() |
四、最佳实践
优先使用本地广播:
- 应用内部通信(如刷新 UI、传递数据)首选本地广播。
- 涉及敏感数据(如用户信息、位置)必须使用本地广播。
仅在必要时使用全局广播:
- 与系统或其他应用交互时使用(如监听网络变化、文件下载完成)。
- 发送全局广播时:
- 使用显式广播 :指定接收者组件名(如
intent.setComponent()
),避免被恶意应用拦截。- 添加权限 :通过
android:permission
限制广播的接收者范围。性能优化:
- 高频发送的广播(如每秒多次)建议用本地广播。
- 避免在全局广播中传递大量数据(跨进程传递会序列化数据,影响性能)。
五、注意事项
- 本地广播无法接收系统事件 :如
android.net.conn.CONNECTIVITY_CHANGE
只能用全局广播监听。- 本地广播不支持有序广播 :
LocalBroadcastManager
没有sendOrderedBroadcast()
方法。- 动态注册限制:本地广播只能动态注册,若需要静态注册(如开机自启动),必须用全局广播。