BroadcastReceiver的应用
BroadcastReceiver的应用
本文是Android四大组件系列的第四篇,主要介绍BroadcastReceiver的基本概念、使用方式以及实际应用场景。
一、BroadcastReceiver基础概念
BroadcastReceiver(广播接收器)是Android四大组件之一,主要用于接收和响应系统或应用发出的广播消息。它允许应用程序接收来自Android系统或其他应用程序的通知,从而实现组件间的松耦合通信。
1.1 BroadcastReceiver的特点
- 松耦合通信:发送方和接收方无需直接关联,通过Intent进行通信
- 全局事件处理:可以接收系统级别的广播事件,如开机启动、网络变化等
- 跨应用通信:可以接收来自其他应用的广播消息
- 生命周期短:广播接收器只在处理广播消息时激活,处理完成后即销毁
1.2 广播的分类
按发送方式分类
- 标准广播(Normal Broadcast):完全异步,所有接收器几乎同时接收到广播
- 有序广播(Ordered Broadcast):按照优先级顺序传递,前面的接收器可以截断广播
按注册方式分类
- 静态注册:在AndroidManifest.xml中注册,应用未启动时也能接收广播
- 动态注册:在代码中注册,随组件生命周期变化,需要手动注销
按作用范围分类
- 系统广播:由Android系统发出的广播,如开机、电量变化等
- 应用内广播:在应用内部发送和接收的广播
- 跨应用广播:可以被其他应用接收的广播
二、BroadcastReceiver的使用方式
2.1 创建BroadcastReceiver
创建BroadcastReceiver需要继承BroadcastReceiver类并实现onReceive()方法:
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "Received broadcast: " + action);
// 获取广播中的数据
String data = intent.getStringExtra("data");
// 处理广播消息
if ("com.example.MY_ACTION".equals(action)) {
// 处理自定义广播
Toast.makeText(context, "Received: " + data, Toast.LENGTH_SHORT).show();
} else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
// 处理系统广播
Log.d(TAG, "System boot completed");
}
}
}
2.2 静态注册BroadcastReceiver
在AndroidManifest.xml中注册广播接收器:
<receiver
android:name=".MyReceiver"
android:exported="true">
<intent-filter>
<!-- 自定义广播 -->
<action android:name="com.example.MY_ACTION" />
<!-- 系统广播 -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
注意:从Android 8.0(API级别26)开始,大多数隐式广播(没有明确指定接收器的广播)无法通过静态注册的方式接收,需要使用动态注册。
2.3 动态注册BroadcastReceiver
在代码中动态注册和注销广播接收器:
public class MainActivity extends AppCompatActivity {
private MyReceiver myReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建广播接收器实例
myReceiver = new MyReceiver();
}
@Override
protected void onStart() {
super.onStart();
// 注册广播接收器
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.MY_ACTION");
filter.addAction(Intent.ACTION_BATTERY_LOW);
registerReceiver(myReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
// 注销广播接收器
unregisterReceiver(myReceiver);
}
}
2.4 发送广播
发送标准广播
// 创建Intent对象
Intent intent = new Intent("com.example.MY_ACTION");
// 添加数据
intent.putExtra("data", "Hello Broadcast");
// 发送广播
sendBroadcast(intent);
发送有序广播
// 创建Intent对象
Intent intent = new Intent("com.example.MY_ORDERED_ACTION");
// 添加数据
intent.putExtra("data", "Hello Ordered Broadcast");
// 发送有序广播
sendOrderedBroadcast(intent, null); // 第二个参数是权限
2.5 本地广播(LocalBroadcastManager)
本地广播只在应用内部传递,更加安全高效:
// 创建LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 注册本地广播接收器
IntentFilter filter = new IntentFilter("com.example.LOCAL_ACTION");
localBroadcastManager.registerReceiver(myReceiver, filter);
// 发送本地广播
Intent intent = new Intent("com.example.LOCAL_ACTION");
intent.putExtra("data", "Hello Local Broadcast");
localBroadcastManager.sendBroadcast(intent);
// 注销本地广播接收器
localBroadcastManager.unregisterReceiver(myReceiver);
注意:LocalBroadcastManager已在AndroidX中被废弃,推荐使用LiveData、Flow或其他替代方案。
三、实战案例:网络状态监听
3.1 创建网络状态广播接收器
public class NetworkReceiver extends BroadcastReceiver {
private NetworkCallback networkCallback;
public interface NetworkCallback {
void onNetworkChanged(boolean isConnected, String networkType);
}
public NetworkReceiver(NetworkCallback callback) {
this.networkCallback = callback;
}
@Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
// 检查网络连接状态
boolean isConnected = false;
String networkType = "unknown";
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.isConnected()) {
isConnected = true;
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
networkType = "WiFi";
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
networkType = "Mobile";
}
}
// 通知回调
if (networkCallback != null) {
networkCallback.onNetworkChanged(isConnected, networkType);
}
}
}
}
3.2 在Activity中使用网络状态监听
public class MainActivity extends AppCompatActivity implements NetworkReceiver.NetworkCallback {
private NetworkReceiver networkReceiver;
private TextView networkStatusText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
networkStatusText = findViewById(R.id.network_status);
// 创建网络状态接收器
networkReceiver = new NetworkReceiver(this);
}
@Override
protected void onStart() {
super.onStart();
// 注册网络状态接收器
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(networkReceiver, filter);
// 立即检查当前网络状态
checkNetworkStatus();
}
@Override
protected void onStop() {
super.onStop();
// 注销网络状态接收器
unregisterReceiver(networkReceiver);
}
@Override
public void onNetworkChanged(boolean isConnected, String networkType) {
// 更新UI显示网络状态
if (isConnected) {
networkStatusText.setText("Connected to " + networkType);
networkStatusText.setTextColor(Color.GREEN);
} else {
networkStatusText.setText("No network connection");
networkStatusText.setTextColor(Color.RED);
}
}
private void checkNetworkStatus() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && activeNetwork.isConnected();
String networkType = "unknown";
if (isConnected) {
if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
networkType = "WiFi";
} else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
networkType = "Mobile";
}
}
onNetworkChanged(isConnected, networkType);
}
}
3.3 适配Android 7.0及以上版本
Android 7.0(API级别24)及以上版本对广播接收器有一些限制,特别是对于系统广播。对于网络状态监听,可以使用NetworkCallback替代广播接收器:
public class NetworkMonitor {
private ConnectivityManager connectivityManager;
private NetworkCallback networkCallback;
public interface NetworkStatusCallback {
void onNetworkAvailable();
void onNetworkLost();
}
public NetworkMonitor(Context context, final NetworkStatusCallback callback) {
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 使用NetworkCallback(Android 7.0及以上)
networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
if (callback != null) {
callback.onNetworkAvailable();
}
}
@Override
public void onLost(Network network) {
if (callback != null) {
callback.onNetworkLost();
}
}
};
}
}
public void register() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 注册NetworkCallback
NetworkRequest request = new NetworkRequest.Builder().build();
connectivityManager.registerNetworkCallback(request, networkCallback);
}
}
public void unregister() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && networkCallback != null) {
// 注销NetworkCallback
connectivityManager.unregisterNetworkCallback(networkCallback);
}
}
}
四、常见系统广播及应用场景
4.1 系统启动相关广播
- ACTION_BOOT_COMPLETED:系统启动完成
- 应用场景:启动后台服务、初始化应用数据
- 权限要求:需要
RECEIVE_BOOT_COMPLETED
权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- ACTION_SHUTDOWN:系统关机
- 应用场景:保存重要数据、释放资源
4.2 电源管理相关广播
-
ACTION_BATTERY_LOW:电池电量低
- 应用场景:提醒用户、降低应用耗电
-
ACTION_BATTERY_OKAY:电池电量恢复正常
- 应用场景:恢复正常功能
-
ACTION_POWER_CONNECTED:连接电源
- 应用场景:开始执行耗电任务
-
ACTION_POWER_DISCONNECTED:断开电源
- 应用场景:停止耗电任务、进入省电模式
4.3 网络相关广播
-
CONNECTIVITY_ACTION:网络连接状态变化
- 应用场景:监控网络状态、调整应用行为
- 注意:Android 7.0后需要动态注册
-
WIFI_STATE_CHANGED_ACTION:WiFi状态变化
- 应用场景:监控WiFi开关状态
4.4 应用安装相关广播
-
ACTION_PACKAGE_ADDED:应用安装
- 应用场景:监控新应用安装
-
ACTION_PACKAGE_REMOVED:应用卸载
- 应用场景:清理相关数据
-
ACTION_PACKAGE_REPLACED:应用更新
- 应用场景:检测应用更新,执行迁移操作
五、实战案例:应用内消息推送系统
5.1 创建消息广播发送器
public class MessagePushManager {
private static final String ACTION_NEW_MESSAGE = "com.example.ACTION_NEW_MESSAGE";
private Context context;
public MessagePushManager(Context context) {
this.context = context.getApplicationContext();
}
public void sendMessage(String title, String content, String targetScreen) {
Intent intent = new Intent(ACTION_NEW_MESSAGE);
intent.putExtra("title", title);
intent.putExtra("content", content);
intent.putExtra("target_screen", targetScreen);
intent.putExtra("timestamp", System.currentTimeMillis());
// 使用LocalBroadcastManager发送应用内广播
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
}
5.2 创建消息广播接收器
public class MessageReceiver extends BroadcastReceiver {
private static final String ACTION_NEW_MESSAGE = "com.example.ACTION_NEW_MESSAGE";
private MessageListener listener;
public interface MessageListener {
void onNewMessage(String title, String content, String targetScreen, long timestamp);
}
public MessageReceiver(MessageListener listener) {
this.listener = listener;
}
public static IntentFilter getIntentFilter() {
return new IntentFilter(ACTION_NEW_MESSAGE);
}
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_NEW_MESSAGE.equals(intent.getAction()) && listener != null) {
String title = intent.getStringExtra("title");
String content = intent.getStringExtra("content");
String targetScreen = intent.getStringExtra("target_screen");
long timestamp = intent.getLongExtra("timestamp", 0);
listener.onNewMessage(title, content, targetScreen, timestamp);
}
}
}
5.3 在Activity中使用消息系统
public class MainActivity extends AppCompatActivity implements MessageReceiver.MessageListener {
private MessageReceiver messageReceiver;
private MessagePushManager pushManager;
private TextView messageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
messageView = findViewById(R.id.message_view);
pushManager = new MessagePushManager(this);
messageReceiver = new MessageReceiver(this);
// 发送测试消息按钮
findViewById(R.id.btn_send_message).setOnClickListener(v -> {
pushManager.sendMessage(
"测试标题",
"这是一条测试消息内容",
"MainActivity");
});
}
@Override
protected void onStart() {
super.onStart();
// 注册消息接收器
LocalBroadcastManager.getInstance(this)
.registerReceiver(messageReceiver, MessageReceiver.getIntentFilter());
}
@Override
protected void onStop() {
super.onStop();
// 注销消息接收器
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(messageReceiver);
}
@Override
public void onNewMessage(String title, String content, String targetScreen, long timestamp) {
// 处理接收到的消息
if ("MainActivity".equals(targetScreen) || targetScreen == null) {
String formattedTime = new SimpleDateFormat("HH:mm:ss", Locale.getDefault())
.format(new Date(timestamp));
String message = String.format("[%s] %s\n%s", formattedTime, title, content);
messageView.append(message + "\n\n");
// 自动滚动到底部
final ScrollView scrollView = (ScrollView) messageView.getParent();
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
}
}
}
六、常见面试题解析
6.1 BroadcastReceiver相关面试题
问题1:BroadcastReceiver的两种注册方式有什么区别?
答案:
-
静态注册:
- 在AndroidManifest.xml中声明
- 应用未启动时也能接收广播
- 应用被杀死后仍能接收广播
- Android 8.0后对隐式广播有限制
- 生命周期与应用生命周期无关
-
动态注册:
- 在代码中通过registerReceiver()方法注册
- 只有在应用运行时才能接收广播
- 必须在组件销毁前调用unregisterReceiver()注销
- 生命周期与注册的组件生命周期相关
- 可以接收所有类型的广播
问题2:标准广播和有序广播有什么区别?
答案:
-
标准广播(Normal Broadcast):
- 完全异步执行
- 所有接收器几乎同时接收到广播
- 效率高
- 接收器之间互不干扰
- 无法被拦截
- 通过sendBroadcast()方法发送
-
有序广播(Ordered Broadcast):
- 同步执行
- 按照接收器优先级顺序传递
- 优先级通过intent-filter的android:priority属性设置
- 前面的接收器可以修改广播内容
- 可以被拦截(abortBroadcast())
- 通过sendOrderedBroadcast()方法发送
问题3:如何在BroadcastReceiver中执行耗时操作?
答案:
BroadcastReceiver的onReceive()方法在主线程中执行,不应该执行耗时操作。处理耗时任务的方法有:
-
启动Service:在onReceive()中启动Service处理耗时任务
Intent serviceIntent = new Intent(context, MyService.class); context.startService(serviceIntent);
-
使用WorkManager:调度后台任务
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class).build(); WorkManager.getInstance(context).enqueue(workRequest);
-
使用goAsync():获取PendingResult延长生命周期
@Override public void onReceive(Context context, Intent intent) { final PendingResult pendingResult = goAsync(); new Thread(() -> { // 执行耗时操作 // ... pendingResult.finish(); }).start(); }
问题4:Android 8.0对广播接收器有哪些限制?如何适配?
答案:
Android 8.0(API 26)对广播接收器的主要限制:
-
隐式广播限制:大多数隐式广播(没有明确指定接收器的广播)无法通过静态注册的方式接收
-
例外情况:
- 面向特定应用的显式广播
- 特定系统广播(如BOOT_COMPLETED、ACTION_LOCALE_CHANGED等)
-
适配方法:
- 使用动态注册替代静态注册
- 使用JobScheduler或WorkManager替代部分广播场景
- 对于自定义广播,使用显式Intent指定接收器
- 使用Context.sendBroadcast(Intent, String)方法发送带权限的广播
6.2 实际开发中的广播问题
问题1:如何防止应用外的广播干扰应用内的广播?
答案:
- 使用LocalBroadcastManager(已废弃但仍可用)
- 使用自定义权限:
<!-- 在AndroidManifest.xml中定义权限 --> <permission android:name="com.example.MY_PERMISSION" android:protectionLevel="signature" /> <!-- 在接收器中使用权限 --> <receiver android:name=".MyReceiver" android:permission="com.example.MY_PERMISSION" />
- 使用显式Intent:明确指定接收器组件
- 使用其他通信机制:如LiveData、EventBus等
问题2:如何在应用被杀死后仍能接收广播?
答案:
- 使用静态注册:在AndroidManifest.xml中注册接收器
- 处理系统限制:
- 针对Android 8.0+,只能接收特定系统广播
- 可能需要申请自启动权限(不同厂商有不同策略)
- 替代方案:
- 使用FCM(Firebase Cloud Messaging)
- 使用WorkManager的周期性任务
- 使用前台服务保持应用活跃
七、开源项目实战分析
7.1 分析EventBus中的广播机制
EventBus是一个流行的事件总线库,它的实现原理与BroadcastReceiver类似,但更加轻量和高效:
// EventBus简化实现
public class SimpleEventBus {
private Map<Class<?>, List<Subscription>> subscriptionsByEventType = new HashMap<>();
// 注册订阅者
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 通过反射找到所有带@Subscribe注解的方法
List<SubscriberMethod> methods = findSubscriberMethods(subscriberClass);
for (SubscriberMethod method : methods) {
subscribe(subscriber, method);
}
}
// 添加订阅
private void subscribe(Object subscriber, SubscriberMethod method) {
Class<?> eventType = method.eventType;
Subscription subscription = new Subscription(subscriber, method);
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new ArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
}
subscriptions.add(subscription);
}
// 发布事件(类似于发送广播)
public void post(Object event) {
Class<?> eventClass = event.getClass();
List<Subscription> subscriptions = subscriptionsByEventType.get(eventClass);
if (subscriptions != null) {
for (Subscription subscription : subscriptions) {
postToSubscriber(subscription, event);
}
}
}
// 向订阅者发送事件
private void postToSubscriber(Subscription subscription, Object event) {
try {
subscription.method.method.invoke(subscription.subscriber, event);
} catch (Exception e) {
e.printStackTrace();
}
}
// 注销订阅者
public void unregister(Object subscriber) {
// 从所有事件类型的订阅列表中移除该订阅者
for (List<Subscription> subscriptions : subscriptionsByEventType.values()) {
Iterator<Subscription> iterator = subscriptions.iterator();
while (iterator.hasNext()) {
if (iterator.next().subscriber == subscriber) {
iterator.remove();
}
}
}
}
// 订阅信息类
private static class Subscription {
final Object subscriber;
final SubscriberMethod method;
Subscription(Object subscriber, SubscriberMethod method) {
this.subscriber = subscriber;
this.method = method;
}
}
// 订阅方法类
private static class SubscriberMethod {
final Method method;
final Class<?> eventType;
SubscriberMethod(Method method, Class<?> eventType) {
this.method = method;
this.eventType = eventType;
}
}
}
与BroadcastReceiver相比,EventBus有以下特点:
- 完全在应用内部使用,不涉及系统广播
- 基于反射实现,不需要显式注册IntentFilter
- 支持更细粒度的事件类型(基于类型而非Action字符串)
- 性能更高,没有系统开销
7.2 分析RxJava中的事件处理机制
RxJava也可以用于替代BroadcastReceiver实现应用内通信:
// 创建事件总线
public class RxBus {
private static final RxBus INSTANCE = new RxBus();
private final Subject<Object> bus = PublishSubject.create().toSerialized();
private RxBus() {
// 私有构造函数
}
public static RxBus getInstance() {
return INSTANCE;
}
// 发送事件
public void post(Object event) {
bus.onNext(event);
}
// 订阅事件
public <T> Observable<T> toObservable(Class<T> eventType) {
return bus.ofType(eventType);
}
}
// 使用示例
public class MainActivity extends AppCompatActivity {
private Disposable subscription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 订阅事件
subscription = RxBus.getInstance().toObservable(MessageEvent.class)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> {
// 处理接收到的事件
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
});
// 发送事件按钮
findViewById(R.id.btn_send).setOnClickListener(v -> {
RxBus.getInstance().post(new MessageEvent("Hello RxBus!"));
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 取消订阅
if (subscription != null && !subscription.isDisposed()) {
subscription.dispose();
}
}
// 事件类
public static class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
}
八、总结
BroadcastReceiver是Android中用于接收广播消息的重要组件,它提供了一种松耦合的通信机制,使应用能够响应系统事件或其他应用发出的广播。
在实际开发中,我们需要根据不同场景选择合适的广播接收方式:
- 系统广播:使用动态注册方式,在应用运行期间接收系统事件
- 应用内通信:考虑使用LiveData、EventBus等替代方案,性能更好
- 跨应用通信:使用显式广播并添加权限控制,确保安全性
随着Android版本的更新,广播接收器的使用受到了越来越多的限制,特别是在Android 8.0及以上版本。开发者需要了解这些限制并采取适当的适配措施,如使用动态注册替代静态注册,或使用其他组件(如JobScheduler、WorkManager)替代部分广播场景。
总的来说,BroadcastReceiver是Android系统中不可或缺的组件,合理使用它可以帮助我们构建响应性更强、交互更灵活的应用程序。