24、BroadcastReceiver:静态注册与动态注册、有序广播与无序广播、Android 8.0+隐式广播限制

BroadcastReceiver,说白了就是Android系统里的「消息喇叭」。某个事件发生了,系统吼一嗓子,所有感兴趣的App都能听到。我刚开始做Android开发时,觉得这玩意儿挺简单,不就是收个广播嘛。后来踩了几个坑才发现,这里面的门道还真不少。

今天咱们就把广播接收器的几个核心知识点掰扯清楚:静态注册和动态注册到底怎么选?有序广播和无序广播有啥区别?Android 8.0之后为什么好多广播收不到了?

静态注册 vs 动态注册

这两种注册方式,是我在项目中经常需要权衡的。先看个对比表格,心里有个底:

对比项 静态注册 动态注册
注册方式 在AndroidManifest.xml中声明 在代码中调用registerReceiver()
生命周期 随App安装而存在,即使App未运行 随组件的生命周期,需手动解注册
接收时机 App未启动也能接收(部分受限) 组件处于活跃状态时才能接收
典型场景 开机广播、安装/卸载广播 网络变化、屏幕亮灭等实时性要求高的场景

静态注册,就是在AndroidManifest.xml里写个<receiver>标签。像这样:

<receiver android:name=".MyBootReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

这样做的好处是,App没启动也能收到广播。我记得有个项目需要监听设备开机,然后启动一个后台服务。用静态注册就特别合适,用户一开机,广播就来了。

但要注意,Android 8.0之后,静态注册能收的广播越来越少了。这个后面细说。

动态注册,是在代码里写的:

// 注册
IntentFilter filter = new IntentFilter("com.example.MY_CUSTOM_ACTION");
MyReceiver receiver = new MyReceiver();
registerReceiver(receiver, filter);

// 别忘了解注册
@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}

我个人习惯用动态注册。为什么呢?因为它的生命周期可控。你在Activity的onResume()里注册,onPause()里解注册,就不会出现「Activity已经销毁了,广播还在跑」的尴尬情况。

⚠️ 重要提醒:动态注册后一定要解注册!我曾经见过一个同事,在Activity里注册了广播,但忘了在onDestroy里unregister。结果Activity被销毁了,广播接收器还活着,每次收到广播都会抛异常。这种内存泄漏的坑,踩一次就记住了。

有序广播 vs 无序广播

这个区别,用一个生活场景就能说清楚。

无序广播,就像在食堂里喊了一声「开饭了」。所有人同时听到,同时响应,谁先谁后无所谓。用sendBroadcast()发送。

有序广播,就像传话游戏。第一个人传给第二个人,第二个人传给第三个人,有先后顺序。而且每个人都可以截断消息,不让后面的人知道。用sendOrderedBroadcast()发送。

看代码:

// 发送无序广播
Intent intent = new Intent("com.example.MY_ACTION");
sendBroadcast(intent);

// 发送有序广播
Intent intent = new Intent("com.example.MY_ORDERED_ACTION");
sendOrderedBroadcast(intent, null);

有序广播的接收器可以设置优先级:

<receiver android:name=".HighPriorityReceiver">
    <intent-filter android:priority="100">
        <action android:name="com.example.MY_ORDERED_ACTION" />
    </intent-filter>
</receiver>

优先级高的先收到。在接收器里,可以调用abortBroadcast()来截断广播:

@Override
public void onReceive(Context context, Intent intent) {
    // 处理完自己的逻辑后,截断广播
    abortBroadcast();
}

我在项目中用过有序广播做「消息拦截」。比如一个支付结果广播,先让安全模块检查,没问题再让UI模块更新界面。安全模块如果发现异常,直接abortBroadcast(),UI模块就收不到了。这种设计,说白了就是责任链模式。

💡 小技巧:有序广播的优先级范围是-1000到1000,数值越大优先级越高。但别太依赖这个,因为不同厂商的ROM可能会有差异。

Android 8.0+ 隐式广播限制

嗯,这里要注意。Android 8.0(API 26)之后,Google对广播做了大手术。大部分隐式广播,静态注册已经收不到了。

什么叫隐式广播?就是只通过action字符串匹配,不指定具体包名的广播。比如:

// 隐式广播 —— 只指定action
Intent intent = new Intent("com.example.SOME_ACTION");
sendBroadcast(intent);

而显式广播是指定了目标包名的:

// 显式广播 —— 指定了包名
Intent intent = new Intent("com.example.SOME_ACTION");
intent.setPackage("com.example.targetapp");
sendBroadcast(intent);

Android 8.0的限制是:静态注册的BroadcastReceiver,无法接收隐式广播。但动态注册不受影响。

为什么会这样?说白了,Google是为了省电和提升性能。你想啊,每个App都静态注册一堆广播,系统每次发广播都要唤醒一堆App,电量就这么耗没了。

那哪些广播还能用静态注册呢?Google留了一些白名单,比如:

  • BOOT_COMPLETED —— 开机广播
  • LOCKED_BOOT_COMPLETED —— 锁屏状态下的开机完成
  • QUICKBOOT_POWERON —— 快速开机
  • 一些系统级的、与安全相关的广播

完整的白名单,可以去官方文档查。我建议你直接记住一句话:能用动态注册就别用静态注册。除非你真的需要App没启动就收广播,而且那个广播在白名单里。

核心结论:

  • 动态注册是主流,生命周期可控,不受8.0限制
  • 静态注册只用于特定场景(开机、安装等),且受白名单限制
  • 有序广播适合需要优先级和拦截的场景
  • 无序广播适合一对多的通知场景

最后,画个图总结一下今天的内容:

BroadcastReceiver 知识体系 注册方式 广播类型 静态注册 AndroidManifest.xml 动态注册 registerReceiver() 无序广播 sendBroadcast() 有序广播 sendOrderedBroadcast() Android 8.0+ 受限 仅白名单可用 生命周期可控 需手动解注册 同时接收 无顺序、无拦截 按优先级接收 可abortBroadcast() 核心原则:优先使用动态注册,静态注册仅用于必要场景

好了,关于BroadcastReceiver的核心知识点就这些。记住一句话:能用动态就别用静态,能显式就别隐式。这样你的App在Android 8.0以上的设备上,就不会莫名其妙收不到广播了。


公众号:蓝海资料掘金营,微信deep3321