A
第 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 且不在后台限制期内。

常见坑与最佳实践

  1. 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();
        }
    }
  2. unregisterReceiver 漏调:会泄漏 Activity / Fragment。在 onDestroy / onStop 中务必注销。
  3. 静态注册隐式广播不生效:Android 8+ 限制下除白名单外不会触发,必须改动态注册或用 JobScheduler。
  4. exported 未显式声明:Android 12+ 安装失败。
  5. 使用 LocalBroadcastManager 已废弃:迁移到 LiveData / Flow / 自定义观察者。
  6. 跨应用广播不加权限:任意 App 都能监听你的敏感事件。务必加 signature 权限。
  7. 用广播替代 EventBus 跨组件通信:广播性能差且不支持泛型,应用内通信首选 LiveData/Flow。
  8. Intent.setPackage 缺失:跨应用隐式广播会扩散到系统所有 App,泄露业务事件。优先 setPackage(getPackageName())。
  9. sendStickyBroadcast 已废弃:Android 5+ 已不推荐,应使用 LiveData 持久化数据。
  10. 在 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)等关键能力。