Android广播机制全解析:标准/有序/动态/静态注册一次理清

📅 发布时间:2026/9/9 19:24:27
Android广播机制全解析:标准/有序/动态/静态注册一次理清 这篇是安卓基础系列的第23篇继续聊广播。前几篇我们把Activity、Service、Fragment这些大块头都过了一遍到了广播这里好多初学者会卡在一个问题上广播到底分几种什么时候用哪种为什么有时候我在清单文件里注册了Receiver却死活收不到开机广播这期内容不搞虚的就围绕“广播类别”这件事把标准广播、有序广播、动态注册、静态注册、本地广播这些概念一次性理清。你看完至少能回答面试官一个连环问“广播有哪几种普通广播和有序广播有什么区别Android 8.0之后静态注册为什么失效了”顺便也能把项目里广播收不到、优先级不生效这类坑给踩平。1. 广播机制到底解决了什么问题1.1 一个让系统组件互相传话的信使安卓的广播本质上是应用之间、系统与应用之间的一种消息传递机制。它很像我们上学时教室里的广播喇叭校长在广播室说一句话全校各个教室只要把喇叭开着注册了接收都能听到。在安卓里那个“校长”就是发送方发送方通过Context.sendBroadcast(intent)把一条Intent扔出去“全校喇叭”就是各个注册了对应Action的BroadcastReceiver。这套机制解决的最大痛点是调用方和被调用方不需要知道彼此的存在。举个最常见例子系统电量低于20%时Android系统会发送一个ACTION_BATTERY_LOW广播。你的应用并没有被系统“直接调用”但只要在合适的位置注册了接收器就能感知到这个事件然后弹出低电量提醒或者自动保存数据。这种一对多、低耦合的通信方式是回调接口和直接调用做不到的。1.2 广播的三个核心角色要理解广播类别得先把参与方认清。一次完整的广播流程里有三方角色发送方通过sendBroadcast()、sendOrderedBroadcast()等方法发出Intent的对象。发送方不需要知道谁会接收。接收方继承BroadcastReceiver并实现onReceive()方法的组件。接收方通过注册表达“我想听什么指令”。系统AMS安卓系统里的ActivityManagerService负责“分发”。发送方把Intent扔给系统系统根据Intent携带的Action去匹配所有注册的接收器然后逐个调用它们的onReceive()。这三方的分工决定了广播的整个生命周期。特别提醒一件事onReceive()是跑在主线程的官方给的时间窗口很短如果你在里面做网络请求或者大的文件操作轻则卡顿重则直接ANR。别把通知栏快捷开关、开机自启这种看似简单的活儿干成连环车祸现场后面我会专门说这个问题。这里还有必要做个名词澄清。安卓里的“广播”指的是BroadcastReceiver这套组件机制它跟网络里的广播域、BLE里的广播包是两码事。很多人搜“广播域”误入安卓开发区看半天觉得莫名其妙就是因为没分清语境。做嵌入式或者网络的同学看到“单播、多播、广播的区别”那是另一套知识体系别混着学。2. 按发送方式划分标准广播与有序广播这是面试里问得最多的一对概念。按发送方式广播可以分为标准广播Normal Broadcast和有序广播Ordered Broadcast。2.1 标准广播异步无序人人有份标准广播通过sendBroadcast(intent)发送它的特点是系统把广播发出去之后所有匹配到的接收器都会收到这条消息接收器之间没有顺序关系也没有办法拦截这条广播让其他人收不到。打个比方你往一个微信群里发了条消息“明天团建”群里每个人都收到了你没法控制谁先看到谁后看到也没法让张三发过的消息在李四那里消失。标准广播就是你发出去就完事完全异步发送方不会等待接收方处理结果。标准广播在代码里长这样Intent intent new Intent(com.example.myapp.MY_NORMAL_ACTION); intent.putExtra(message, hello broadcast); sendBroadcast(intent);接收方在onReceive()里拿到Intent取出Action和数据public class NormalReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { String action intent.getAction(); if (com.example.myapp.MY_NORMAL_ACTION.equals(action)) { String msg intent.getStringExtra(message); Log.d(NormalReceiver, 收到标准广播: msg); } } }这个类本身没什么特殊要求关键是你得让系统知道它的存在所以还要配注册逻辑。注册方式我在第3部分详细讲这里先记住标准广播适合“通知一下大家”的场景比如数据下载完成、用户登录状态变化不需要关心接收方处理结果。2.2 有序广播串行接收可拦截可改写有序广播走的是sendOrderedBroadcast(intent, receiverPermission)这条路它跟标准广播最大的区别在于所有接收器按照优先级从高到低排队一个接一个地收到广播并且在任意一环都可以终止广播继续传递也可以往Intent里写入数据传给下一环。继续用微信群举例群里发通知但这次是按职位顺序传话。大领导先看觉得这条不合适一个“撤回”操作后面的人全看不见了如果领导觉得没问题还能在通知后面加一句“各部门必须执行”再传给下一级。这就是有序广播。发送有序广播的代码Intent intent new Intent(com.example.myapp.MY_ORDERED_ACTION); intent.putExtra(message, 原始数据); sendOrderedBroadcast(intent, null);第二个参数receiverPermission是权限字符串传null表示不限制接收方权限。如果需要限制只有持有某权限的接收器才能收到可以传一个自定义权限名。接收器里的两个关键操作// 1. 拦截后续接收器收不到 abortBroadcast(); // 2. 改写数据给下一个接收器传东西 setResultCode(Activity.RESULT_OK); setResultData(我改过的数据);下一个接收器取数的时候用的就不是intent.getStringExtra()了而是int resultCode getResultCode(); String resultData getResultData();优先级怎么定动态注册通过IntentFilter.setPriority()静态注册在intent-filter里配android:priority属性数字越大优先级越高取值范围是-1000到1000。注意优先级只在有序广播时才有意义标准广播里你就算配了priority接收顺序也不保证。这是很多人踩坑的地方以为设了高优先级就能抢先收到普通广播实际是白忙一场。还有个小技巧sendOrderedBroadcast还有一个带resultReceiver参数的重载可以指定一个“最终接收器”等所有接收器跑完后在最后收尾拿到最终数据BroadcastReceiver finalReceiver new BroadcastReceiver() { Override public void onReceive(Context context, Intent intent) { String finalData getResultData(); Log.d(Final, 最终结果: finalData); } }; sendOrderedBroadcast(intent, null, finalReceiver, null, Activity.RESULT_OK, null, null);这个在多个接收器协作处理数据的场景非常有用比如A先校验合法性B再补全信息最后由finalReceiver统一入库。2.3 两者怎么选先问自己三个问题项目里遇到一个场景先别急着写代码问自己三个问题这条消息需要让多个接收方处理吗如果只需要单个组件响应其实不一定用广播本地回调或者事件总线可能更轻量。各接收方之间有没有先后依赖有依赖就上sendOrderedBroadcast没依赖就标准广播。接收方会不会有外部应用如果广播只是应用内部消息别用全局广播优先考虑本地广播或者现代替代方案第4部分说。有一种常见的错误想法是反正广播发送方和接收方都不知道彼此所以一律用标准广播最省事。实际上滥用标准广播会让系统做大量匹配分发尤其在高频场景下比如播放器进度更新非常浪费性能。广播不是万能的它只是四大组件通信方案里的一种。3. 按注册方式划分动态注册与静态注册广播接收器自身不会凭空生效必须经过“注册”这一步。注册方式分为动态注册和静态注册这也是面试里必考的点。3.1 动态注册实战跟着生命周期走动态注册的意思是在代码里通过Context.registerReceiver()方法注册接收器注册之后接收器才生效取消注册之后就不再接收广播。这块必须配合生命周期使用标准做法是在onStart()或onResume()里注册在onStop()或onPause()里注销因为不在合适时机注销会导致内存泄漏。先写一个接收器监听系统每分钟发出的时间广播ACTION_TIME_TICKpublic class MainActivity extends AppCompatActivity { private final BroadcastReceiver timeReceiver new BroadcastReceiver() { Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_TIME_TICK.equals(intent.getAction())) { Log.d(TimeReceiver, 系统时间变化当前整分钟刷新); } } }; Override protected void onStart() { super.onStart(); IntentFilter filter new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); // targetSdk 34 以上必须显式指定接收器是否对外导出 registerReceiver(timeReceiver, filter, Context.RECEIVER_NOT_EXPORTED); } Override protected void onStop() { super.onStop(); unregisterReceiver(timeReceiver); } }这里有个版本细节很关键如果你的targetSdkVersion是34Android 14及以上动态注册必须带第三个参数Context.RECEIVER_EXPORTED或Context.RECEIVER_NOT_EXPORTED否则会抛SecurityException。系统强制你声明这个广播接收器是否对其它应用开放。像我上面监听ACTION_TIME_TICK是纯系统广播应用自己监听自己使用用RECEIVER_NOT_EXPORTED就够了。可能有人问为什么用RECEIVER_NOT_EXPORTED这个标志的含义是“只允许同一个应用或者系统UID发送的广播进入”非常适合应用内部和系统广播场景。如果确实需要接收其他应用的广播再用RECEIVER_EXPORTED但那样会暴露接收能力要额外注意安全。3.2 静态注册实战清单文件里的小本本静态注册是在AndroidManifest.xml里通过receiver标签声明接收器相当于你给应用装了一个“永久喇叭”——即使应用进程被杀掉系统也可能通过反射把进程拉起来让接收器处理广播。最常见的静态注册场景是开机自启先申请权限uses-permission android:nameandroid.permission.RECEIVE_BOOT_COMPLETED /再注册接收器receiver android:name.BootReceiver android:enabledtrue android:exportedtrue intent-filter action android:nameandroid.intent.action.BOOT_COMPLETED / /intent-filter /receiver对应接收器代码public class BootReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { // 开机完成后拉起核心服务 Intent service new Intent(context, CoreService.class); context.startForegroundService(service); } } }这里必须说的一个重点Android 8.0API 26之后绝大多数隐式广播已经不支持静态注册了。系统做了个大改革把所有静态注册的隐式广播拉黑只剩下一小撮例外还开着绿灯比如BOOT_COMPLETED开机完成LOCKED_BOOT_COMPLETED解锁前的开机完成MY_PACKAGE_REPLACED应用自身被覆盖安装ACTION_SHUTDOWN关机LOCALE_CHANGED系统语言变化USB_ACCESSORY_ATTACHED/USB_DEVICE_ATTACHEDUSB设备插拔这些之外的通通不行。你可以静态注册自定义Action吗在Android 8.0以下可以8.0以上就不再收到隐式广播了。不过如果你发的是显式广播用setPackage(getPackageName())限制为自家应用或者直接用ComponentName指定接收器静态注册依然有效。这种设计是为了防止恶意应用在后台频繁被拉起保护电量。还有一个坑targetSdkVersion在31Android 12及以上时凡是带有intent-filter的四大组件必须显式声明android:exported属性。BootReceiver这种需要接收系统广播的必须设置为true如果只给自己应用发广播就设false。不写这个属性安装直接报错。3.3 动态与静态怎么选一张表看清对比维度动态注册静态注册注册位置代码里registerReceiverAndroidManifest.xml生命周期跟随组件生命周期需注销常驻进程被杀死也可能拉起接收隐式广播支持Android 8.0起大部分不支持系统广播监听适合屏幕亮灭、时间变化等高频只适合开机、安装、语言切换等低频内存泄漏风险不注销会泄漏无此风险耗电影响注册期间才接收常驻系统负担大我的习惯是能动态注册就不静态注册。动态注册把接收器的生命力和组件绑死不会出现Activity已经销毁了还傻傻处理广播的情况。只有开机自启、应用更新后重新调度、UX自动安装这类系统级事件才考虑静态注册。4. 按作用范围划分本地广播与全局广播广播按作用范围还能分成本地广播和全局广播。在Android开发的历史里本地广播曾是一个标准答案后来又被官方亲手推翻这个演进过程非常有意思。4.1 本地广播曾经的安全屋本地广播是指广播只在应用内部传递不会跑到应用外面去其他应用收不到。早期的方案是LocalBroadcastManager可以把它理解成一个私有化的消息总线内部用Handler实现效率比系统广播高也能防止外部应用嗅探你的消息。使用方式如下// 注册 LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter); // 发送 LocalBroadcastManager.getInstance(this).sendBroadcast(intent); // 注销 LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);这套API用起来很简便内部自己走Handler不走AMS所以性能比全局广播好也不会把消息泄漏到外部应用。但是LocalBroadcastManager已经被官方标记为废弃了。androidx.localbroadcastmanager包最后更新停留在1.1.0Google明确建议开发者迁移到LiveData或Flow。为什么废弃因为它的作用就是“应用内一对一、一对多通信”而LiveData和协程Flow完全可以覆盖这些场景还天然支持生命周期感知不需要手动注册注销也不用手动处理线程切换。继续在新项目里用LocalBroadcastManager没什么技术上的好处反而会被code review的人问一句“这里为什么不直接用LiveData”。4.2 现在的替代方案LiveData与SharedFlow如果只是想应用内部发个消息我的建议是分情况选需求是UI层感知数据变化直接用LiveData。登录状态、列表刷新、用户资料更新这些都是典型的UI状态变更LiveData配合ViewModel用起来很顺手class MainViewModel : ViewModel() { private val _refreshEvent MutableLiveDataBoolean() val refreshEvent: LiveDataBoolean _refreshEvent fun triggerRefresh() { _refreshEvent.value true } } // Activity 里观察 viewModel.refreshEvent.observe(this) { needRefresh - if (needRefresh) loadData() }如果需求是“非UI层的一次性事件”比如网络库收到推送想通知仓库层更新缓存更推荐用SharedFlow。SharedFlow是热流不管有没有订阅者都会把事件发出去而且支持replay让新订阅者拿到最近一条事件比LiveData处理一次性事件要灵活class MessageBus { companion object { val refreshFlow MutableSharedFlowString(extraBufferCapacity 10) } } // 发送 MessageBus.refreshFlow.emit(refresh_now) // 收集 lifecycleScope.launch { MessageBus.refreshFlow.collect { msg - Log.d(Bus, 收到: $msg) } }那全局广播还用来干嘛跨应用通信或者系统事件监听。比如你的应用要接收另一个应用发的数据包或者要响应开机、安装、网络切换这些系统事件还得走sendBroadcast()。但注意安全全局广播默认是“广播出去谁都能听”如果你发的是应用内的敏感信息一定要setPackage(getPackageName())把自己锁定或者用显式广播直接指定接收组件。显式广播的正确姿势Intent intent new Intent(com.example.myapp.INTERNAL_ACTION); intent.setPackage(getPackageName()); // 只允许本应用接收 context.sendBroadcast(intent);这样的话即使有个恶意应用监听所有Action它也会因为包名对不上而不是你的目标。5. 系统广播与自定义广播两类高频实操对象广播类别里除了按发送方式、注册方式、作用范围划分还有一个特别重要的角度广播事件本身是谁定义的。这里分系统广播和自定义广播。5.1 系统广播常用Action清单系统广播是安卓系统在各个关键节点发出来的事件接收方写好Action字符串就能监听。我梳理了一份日常开发里出现频率最高的清单Action触发时机注册方式备注ACTION_BOOT_COMPLETED系统启动完成静态需要RECEIVE_BOOT_COMPLETED权限ACTION_TIME_TICK每分钟时间变化动态不能静态注册ACTION_SCREEN_ON/ACTION_SCREEN_OFF屏幕亮/灭动态不能静态注册ACTION_BATTERY_LOW/ACTION_BATTERY_OKAY电量低/恢复动态不能静态注册ACTION_PACKAGE_ADDED/ACTION_PACKAGE_REMOVED应用安装/卸载动态或静态静态注册受限需指定schemeACTION_HEADSET_PLUG耳机插拔动态传统方案新版有线音频建议用AudioManagerACTION_CONNECTIVITY_CHANGE网络连接变化动态官方已不建议监听用ConnectivityManager回调更稳ACTION_MY_PACKAGE_REPLACED自身应用更新静态更新后做数据迁移常用ACTION_LOCALE_CHANGED系统语言变化静态需重新初始化资源ACTION_SHUTDOWN系统关机静态做收尾工作注意几个细节ACTION_TIME_TICK、ACTION_SCREEN_ON/OFF只能动态注册静态注册写了也白写。ACTION_CONNECTIVITY_CHANGE虽然可以动态注册但官方更推荐使用ConnectivityManager.NetworkCallback毕竟现在网络类型五花八门单纯监听Action拿不到足够的状态信息。监听ACTION_PACKAGE_ADDED这类跟包相关的广播要在IntentFilter里加addDataScheme(package)不然收不到IntentFilter filter new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addDataScheme(package); registerReceiver(packageReceiver, filter);系统广播是理解安卓事件驱动模型的最佳入口。建议新手自己动手写一个小Demo动态注册监听屏幕亮灭和电量变化日志里观察事件触发顺序比死记硬背清单有效得多。5.2 自定义广播的Action规范与发送除了听系统广播应用之间还能自己定义Action来通信。自定义广播的Action字符串不是随便写个hello就行行业里通常用包名做前缀格式类似com.example.myapp.CUSTOM_ACTION这样能避免跟其他应用的Action撞车。比如你要写一个下载完成的广播// 发送 Intent intent new Intent(com.example.myapp.DOWNLOAD_COMPLETE); intent.putExtra(fileId, 10086); intent.setPackage(getPackageName()); sendBroadcast(intent);// 接收 public class DownloadReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { if (com.example.myapp.DOWNLOAD_COMPLETE.equals(intent.getAction())) { int fileId intent.getIntExtra(fileId, -1); // 更新数据库 // 发通知栏提醒 } } }自定义广播如果是给自己应用用的强烈建议加上setPackage(getPackageName())收窄范围。如果不加等于把内部消息明文广播给整个系统任何安装了监听器的应用都能顺手偷走你的数据。现在很多App通信都改走深链、互拉等方案但自定义广播在处理同一个应用不同进程之间的通信时仍然有用比如多进程架构下通知另一个进程刷新数据。5.3 粘性广播一个埋掉的雷老早以前的安卓版本还有一个叫粘性广播Sticky Broadcast的分类发送方式是sendStickyBroadcast()。它的特点是广播发送完之后不会立刻消失而是像口香糖一样粘在系统里之后注册的新接收器也能立刻收到这条广播。听起来好像很美好但问题是它存在严重的安全和隐私漏洞任何应用都能拿到你发的粘性数据容易造成敏感信息泄露。这套API早就被标记废弃了现在的新项目里见到它的概率极低。如果你在维护老项目遇到sendStickyBroadcast()建议尽快替换成正常的广播或者LiveData。面试时如果被问到直接说“已经废弃的机制不用”就够了不用花太多时间研究。6. 常见问题与排查技巧实录6.1 广播没收到从注册、发送到匹配的逐个排查广播没收到是新手问烂了的问题。我每次排查都按这个顺序来第一步检查Action字符串是否完全一致。别小看这个多一个空格、大小写不同都会匹配失败。Action的推荐写法是包名加常量然后发送和接收都引用同一个常量避免拼写错误。第二步检查注册是否成功。动态注册要看代码有没有执行到registerReceiver比如放在某个按钮回调里但你压根没点过那个按钮或者注册了又注销了时序对不上。静态注册要确认AndroidManifest.xml里的receiver标签没写错包名路径对不对。第三步检查Android版本限制。如果你静态注册了一个自定义Action然后在API 26以上的设备测试收不到是正常的因为系统不允许这种玩法。第四步检查应用是否被“停止”了。用户在系统设置里强行停止应用后应用进程被冻结静态注册的接收器也不会被唤醒。这是设计如此别觉得是Bug。第五步检查Exported标志。targetSdkVersion31之后接收器没写android:exported或动态注册没传FLAG直接抛异常或者收不到去logcat里搜索Exception关键字能快速看到原因。还有一个经常被忽略的场景发送时机早于注册时机。比如你在onCreate()里发广播接收器在onResume()里才注册那这条广播发出时系统根本不知道有接收者自然收不到。解决思路是调整注册时机或者用第4部分提到的LiveData/Flow它们不依赖注册时序。6.2 动态注册的泄漏问题Activity已经没了还在收这个坑多见于Activity里注册了接收器却在onDestroy()里忘记注销。结果Activity实例明明已经被回收了但因为BroadcastReceiver还持有它的引用导致GC无法回收整棵对象树时间一长就OOM。正确的绑定方式是onStart()里注册onStop()里注销或者onResume()里注册onPause()里注销。把注册注销放在onCreate()和onDestroy()看起来对称很美观但风险很大特别是用户按Home键退出时Activity只是stop并没有destroy广播依然在收白白耗电。如果你的接收器需要处理耗时逻辑有两种改进思路要么让onReceive()里发个消息给WorkManager去执行任务要么直接用goAsync()告诉系统我需要一点时间处理等处理完再调用PendingResult.finish()。但goAsync()只适合延长几秒不适合跑长任务。Override public void onReceive(Context context, Intent intent) { final PendingResult result goAsync(); new Thread(() - { // 耗时操作 result.finish(); }).start(); }不要在主线程里开线程就以为万事大吉了goAsync()之后仍然有超时限制官方没规定死时间但超过十秒一样危险。真要跑长任务老老实实startService()或者WorkManager。6.3 优先级不生效先确认是不是有序广播“我设置了high priority为什么没抢到先手”这个问题我回答过很多次。优先级只在有序广播里才有意义。用sendBroadcast()发的是标准广播所有接收者之间无序无优先级可言如果用的是sendOrderedBroadcast()还要注意优先级数字是否真的写进去了。动态注册设置优先级IntentFilter filter new IntentFilter(com.example.myapp.ORDERED_ACTION); filter.setPriority(500);静态注册设置优先级intent-filter android:priority500 action android:namecom.example.myapp.ORDERED_ACTION / /intent-filter另一个常见迷惑点是两个接收器优先级相同怎么办同优先级下系统不保证顺序按注册先后或系统内部调度来。所以不要依赖优先级解决所有排序问题真需要严格顺序就把上一个接收器处理完的结果通过setResultData()传给下一个用数据驱动下一个动作。6.4 常见广播问题速查表问题现象可能原因解决方案自定义广播收不到Action不一致 / 参数错误统一常量检查setPackage开机广播收不到未声明权限 / 应用被停止 / 机型限制加RECEIVE_BOOT_COMPLETED引导用户加入自启动白名单屏幕广播静态注册没反应此广播不允许静态注册改成动态注册动态注册报SecurityExceptiontargetSdk 34未传FLAG补上RECEIVER_NOT_EXPORTED/RECEIVER_EXPORTED有序广播后续接收器没收到前一个接收器调用了abortBroadcast确认业务是否需要拦截多次快速发送广播丢消息系统广播分发是异步的接收器太重减少广播频率或改用消息队列Activity泄漏未注销接收器onStop/onPause里unregisterReceiver6.5 别在onReceive里做的三件事最后再列几个我在实际项目里看到过的高危操作都是真实踩出来的坑第一别弹对话框。onReceive()执行时可能根本没有Activity在前台直接弹Dialog会出现BadTokenException或者对话框显示在别的应用上面。要提醒用户就去发通知栏通知。如果想启动一个Activity记得加FLAG_ACTIVITY_NEW_TASKIntent activityIntent new Intent(context, ResultActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(activityIntent);第二别绑定Service。onReceive()里调用bindService()会报“Context.bindService() called from BroadcastReceiver without using goAsync()”错误因为接收器生命周期太短Service绑定还没完成接收器就没了。改成startService()或startForegroundService()并注意Android 8.0后台启动Service的限制。第三别用广播做高频率通信。比如拖动进度条每秒发N条广播接收器再更新UI性能和流畅度会非常感人。这种情况下高度怀疑你在做重复造轮子同一个应用内部数据共享用LiveData、StateFlow或者直接回调接口才是正路。广播的定位是“松耦合的全局事件通知”不是“内部数据总线”。说实话广播这套东西本身不难难就难在版本演进后遗留的各种“历史兼容问题”和“边界情况”。我个人的习惯是先判断消息作用域应用内部优先LiveData/Flow需要跟系统或其他应用联动才考虑广播需要开机自启、应用更新等后台事件时静态注册其他能动态就动态。按这条规则走下来90%的广播坑都能绕开。这次先把广播的类别理清了下一期我会接着聊广播的进阶内容包括自定义权限、前台广播受限的具体处理、以及多进程通信里的广播坑。你在项目里碰到过什么广播相关的奇葩问题可以在评论区唠唠我挑典型的下期展开。