第 19 章JAVA40 分钟
BroadcastReceiver 广播
掌握 BroadcastReceiver 静态注册与动态注册、标准广播与有序广播、自定义广播、LocalBroadcastManager 废弃后的替代方案、广播权限与最佳实践限制
学习目标
- 区分静态注册(清单)与动态注册(registerReceiver)的使用场景
- 理解标准广播与有序广播的差异及有序广播的优先级与截断
- 自定义广播与 IntentFilter 配合实现跨组件通信
- 了解 LocalBroadcastManager 废弃原因与替代方案
- 使用广播权限保护敏感广播,遵循 Android 8+ 与 14+ 限制
学习目标
BroadcastReceiver 是 Android 四大组件中最“轻量”的一类,用于响应系统事件或跨进程消息。本章目标:
- 区分静态注册与动态注册的适用场景;
- 理解标准广播与有序广播的差异;
- 自定义广播并通过 IntentFilter 接收;
- 了解
LocalBroadcastManager废弃后的替代方案; - 用广播权限保护敏感广播,遵循 Android 8+ 隐式广播限制。
静态注册 vs 动态注册
1. 静态注册(Manifest)
<!-- AndroidManifest.xml -->
<receiver
android:name=".BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
public class BootReceiver extends BroadcastReceiver {
private static final String TAG = "BootReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
Log.d(TAG, "Boot completed");
// 仅能在 Android 10 之前自由启动 Service
// Android 8+ 不能在后台启动 Service
Intent serviceIntent = new Intent(context, KeepAliveService.class);
ContextCompat.startForegroundService(context, serviceIntent);
}
}
}
特点:
- App 安装即生效,无需启动;
- 在 Android 8+ 上除少数白名单(如
BOOT_COMPLETED、USB_DEVICE_ATTACHED)外,隐式广播不再能静态注册; - 适合“开机启动”、“应用更新”等系统级事件。
2. 动态注册(registerReceiver)
public class ConnectivityActivity extends AppCompatActivity {
private NetworkChangeReceiver mReceiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mReceiver = new NetworkChangeReceiver();
IntentFilter filter = new IntentFilter();
// Android 7+ 推荐用 ConnectivityManager.NetworkCallback 替代
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mReceiver, filter,
ContextCompat.RECEIVER_NOT_EXPORTED);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReceiver != null) {
unregisterReceiver(mReceiver);
mReceiver = null;
}
}
private static class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 检查网络状态
}
}
}
特点:
- 跟随组件生命周期,必须
unregisterReceiver; - 性能更好,可动态调整 IntentFilter;
- Android 13+ 必须显式声明
RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED。
3. 何时选择
| 场景 | 推荐方式 |
|---|---|
| 开机自启、应用更新 | 静态 |
| 网络变化、电量变化、屏幕亮灭 | 动态(或现代 API 替代) |
| 自定义业务广播 | 动态(同进程内) |
| 跨进程业务通信 | 动态 + exported + 权限 |
标准广播 vs 有序广播
1. 标准广播(Normal)
Intent intent = new Intent("com.example.myapp.ACTION_DATA_UPDATED");
intent.setPackage(getPackageName()); // 限制本应用
context.sendBroadcast(intent);
特点:
- 异步、并行投递给所有接收者;
- 接收者之间无顺序、不可截断;
- 性能高,最常用。
2. 有序广播(Ordered)
Intent intent = new Intent("com.example.myapp.ACTION_PRIORITY");
intent.setPackage(getPackageName());
context.sendOrderedBroadcast(intent, null);
接收者通过 android:priority 排序,高优先级先收到:
<receiver android:name=".HighPriorityReceiver">
<intent-filter android:priority="100">
<action android:name="com.example.myapp.ACTION_PRIORITY" />
</intent-filter>
</receiver>
public class HighPriorityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 可以追加数据
setResultData("processed by High");
// 也可以截断,让低优先级接收者收不到
abortBroadcast();
}
}
特点:
- 按优先级串行投递;
- 可通过
setResultData / getResultData传递中间结果; - 可通过
abortBroadcast()截断后续投递; - 性能差,慎用。
自定义广播
1. 发送方
public class OrderService extends Service {
public static final String ACTION_ORDER_CREATED =
"com.example.myapp.ACTION_ORDER_CREATED";
public static final String EXTRA_ORDER_ID = "extra_order_id";
private void notifyOrderCreated(String orderId) {
Intent intent = new Intent(ACTION_ORDER_CREATED);
intent.setPackage(getPackageName()); // 限制同 App 接收
intent.putExtra(EXTRA_ORDER_ID, orderId);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
// 或 context.sendBroadcast(intent)
}
}
2. 接收方
public class MainActivity extends AppCompatActivity {
private OrderCreatedReceiver mReceiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mReceiver = new OrderCreatedReceiver();
IntentFilter filter = new IntentFilter(OrderService.ACTION_ORDER_CREATED);
ContextCompat.registerReceiver(this, mReceiver, filter,
ContextCompat.RECEIVER_NOT_EXPORTED);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
}
private class OrderCreatedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String orderId = intent.getStringExtra(OrderService.EXTRA_ORDER_ID);
showOrderToast(orderId);
}
}
private void showOrderToast(String orderId) {
Toast.makeText(this, "新订单:" + orderId, Toast.LENGTH_SHORT).show();
}
}
LocalBroadcastManager 替代方案
1. 废弃背景
LocalBroadcastManager 自 androidx 1.1.0 起被标记为废弃,原因:
- 实现内部用
Handler主线程,与系统 BroadcastReceiver 行为不一致; - 不支持跨进程;
- 在多 BroadcastReceiver 场景性能不如 LiveData / Flow。
2. 替代方案对照
| 场景 | 推荐方案 |
|---|---|
| 同进程内单对多通知 | LiveData / SharedFlow |
| 同进程跨组件一次性事件 | EventBus / 自定义观察者 |
| 跨进程通信 | sendBroadcast + 权限 / Messenger / AIDL |
| 应用内自启动 Service | 改用 PendingIntent / WorkManager |
3. 用 LiveData 实现同进程事件分发
public class OrderBus {
private static class Holder {
static final OrderBus INSTANCE = new OrderBus();
}
public static OrderBus get() { return Holder.INSTANCE; }
private final MutableLiveData<String> mOrderCreated = new MutableLiveData<>();
public LiveData<String> getOrderCreated() { return mOrderCreated; }
public void post(String orderId) {
mOrderCreated.postValue(orderId);
}
}
// 发送
OrderBus.get().post("order_9283");
// 接收
OrderBus.get().getOrderCreated().observe(this, id -> {
showOrderToast(id);
});
广播权限
1. 发送方声明权限
<!-- AndroidManifest.xml -->
<permission
android:name="com.example.myapp.permission.ORDER_EVENT"
android:protectionLevel="signature" />
<uses-permission android:name="com.example.myapp.permission.ORDER_EVENT" />
2. 接收方声明使用权限
<receiver
android:name=".OrderReceiver"
android:permission="com.example.myapp.permission.ORDER_EVENT"
android:exported="true">
<intent-filter>
<action android:name="com.example.myapp.ACTION_ORDER_CREATED" />
</intent-filter>
</receiver>
3. 发送时指定接收方权限
sendBroadcast(intent, "com.example.myapp.permission.ORDER_EVENT");
用
signature级别可保证只有相同签名的 App 能收发,是跨应用广播的最佳实践。
Android 8+ 与 14+ 限制
1. 隐式广播静态注册限制(Android 8+)
Android 8.0 起,除白名单外(如 BOOT_COMPLETED、USB_DEVICE_ATTACHED、LOCALE_CHANGED),其他隐式广播不再能通过 Manifest 静态注册。完整白名单见官方文档 Implicit Broadcast Exceptions。
2. RECEIVER_EXPORTED 强制声明(Android 13+)
// Android 13+ 必须指定其中一个 flag
ContextCompat.registerReceiver(this, receiver, filter,
ContextCompat.RECEIVER_NOT_EXPORTED); // 仅同 App 接收
// 跨 App 接收
ContextCompat.registerReceiver(this, receiver, filter,
ContextCompat.RECEIVER_EXPORTED);
不声明会抛 SecurityException。
3. 后台启动 Activity 限制(Android 10+)
收到广播后调用 startActivity 需要前台上下文或带 FLAG_ACTIVITY_NEW_TASK 且不在后台限制期内。
常见坑与最佳实践
onReceive主线程耗时操作:默认 10 秒 ANR。耗时长请用goAsync()或PendingIntent调度 Service / WorkManager。public class HeavyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { PendingResult pending = goAsync(); new Thread(() -> { try { doHeavyWork(); } finally { pending.finish(); } }).start(); } }unregisterReceiver漏调:会泄漏 Activity / Fragment。在onDestroy/onStop中务必注销。- 静态注册隐式广播不生效:Android 8+ 限制下除白名单外不会触发,必须改动态注册或用 JobScheduler。
exported未显式声明:Android 12+ 安装失败。- 使用
LocalBroadcastManager已废弃:迁移到 LiveData / Flow / 自定义观察者。 - 跨应用广播不加权限:任意 App 都能监听你的敏感事件。务必加
signature权限。 - 用广播替代 EventBus 跨组件通信:广播性能差且不支持泛型,应用内通信首选 LiveData/Flow。
Intent.setPackage缺失:跨应用隐式广播会扩散到系统所有 App,泄露业务事件。优先setPackage(getPackageName())。sendStickyBroadcast已废弃:Android 5+ 已不推荐,应使用 LiveData 持久化数据。- 在 Receiver 中直接
startService:Android 8+ 后台 Service 启动受限。改用ContextCompat.startForegroundService或 WorkManager。
章节小结
- 静态注册用于系统级、App 未启动也需响应的事件(白名单内);动态注册用于应用内通信;
- 标准广播性能好最常用,有序广播按优先级串行可截断,慎用;
- 自定义广播配合
IntentFilter与setPackage实现安全通信; LocalBroadcastManager已废弃,同进程用 LiveData/Flow,跨进程用sendBroadcast+ signature 权限;- Android 13+ 必须声明
RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTED,Android 8+ 隐式广播静态注册被限制; onReceive耗时操作用goAsync()+ 后台线程。
下一章预告
Part 3 核心组件至此完结。Part 4 数据与网络将开启:网络请求(OkHttp / Retrofit)、数据解析(JSON / Gson / Moshi)、本地存储(SharedPreferences / Room / DataStore)、文件与媒体(FileProvider / MediaStore)、加密(EncryptedSharedPreferences)等关键能力。