购物类网站首页效果图网络营销广告案例
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";@Overridepublic 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中注册广播接收器:
<receiverandroid: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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 创建广播接收器实例myReceiver = new MyReceiver();}@Overrideprotected void onStart() {super.onStart();// 注册广播接收器IntentFilter filter = new IntentFilter();filter.addAction("com.example.MY_ACTION");filter.addAction(Intent.ACTION_BATTERY_LOW);registerReceiver(myReceiver, filter);}@Overrideprotected 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;}@Overridepublic 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);networkStatusText = findViewById(R.id.network_status);// 创建网络状态接收器networkReceiver = new NetworkReceiver(this);}@Overrideprotected void onStart() {super.onStart();// 注册网络状态接收器IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);registerReceiver(networkReceiver, filter);// 立即检查当前网络状态checkNetworkStatus();}@Overrideprotected void onStop() {super.onStop();// 注销网络状态接收器unregisterReceiver(networkReceiver);}@Overridepublic 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() {@Overridepublic void onAvailable(Network network) {if (callback != null) {callback.onNetworkAvailable();}}@Overridepublic void onLost(Network network) {if (callback != null) {callback.onNetworkLost();}}};}}public void register() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// 注册NetworkCallbackNetworkRequest request = new NetworkRequest.Builder().build();connectivityManager.registerNetworkCallback(request, networkCallback);}}public void unregister() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && networkCallback != null) {// 注销NetworkCallbackconnectivityManager.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);}@Overridepublic 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;@Overrideprotected 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");});}@Overrideprotected void onStart() {super.onStart();// 注册消息接收器LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, MessageReceiver.getIntentFilter());}@Overrideprotected void onStop() {super.onStop();// 注销消息接收器LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver);}@Overridepublic 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;@Overrideprotected 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!"));});}@Overrideprotected 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系统中不可或缺的组件,合理使用它可以帮助我们构建响应性更强、交互更灵活的应用程序。