第二板块:Android 四大组件标准化学理 | 第九篇:BroadcastReceiver 事件分发与有序广播

第二板块:Android 四大组件标准化学理 | 第九篇:BroadcastReceiver 事件分发与有序广播

所属板块:第二板块 --- Android 四大组件标准化学理

前置知识:第八篇中的 Service 优先级、Binder IPC、AMS 调度机制

本篇定位 :BroadcastReceiver 是 Android 系统中最轻量级、最松散耦合 的组件。它不提供 UI,也不直接执行长时间任务,而是作为一个系统级事件的中转站(Event Hub) 。本篇将彻底拆解广播的异步分发模型AMS 中的队列管理机制静态注册与动态注册的权限鸿沟有序广播(Ordered Broadcast)的拦截与修改机制粘性广播(Sticky Broadcast)的历史遗留问题LocalBroadcastManager 的进程内通信本质 。我们将深入 ActivityManagerService (AMS)BroadcastQueue 的源码级逻辑,揭示广播为何被称为"冷启动炸弹"以及如何正确规避。全程无业务代码、无最佳实践建议,仅保留 Android Framework 的底层定义与系统级调度规范。


1. 核心结论先行(Thesis Statement)

BroadcastReceiver 是 Android 操作系统中用于接收系统或应用发出的异步消息(Intent)的组件。它不是线程 ,也不是服务 ,而是一个由系统服务(AMS)管理的、具有极短生命周期的、运行在主线程中的回调实体

  • Receiver 的本质 :一个事件监听器(Event Listener) 。它监听特定的 Intent 广播,并在接收到广播时执行一段简短的逻辑。
  • 分发模型异步发布-订阅模型(Pub-Sub)。发送者(Publisher)发出广播,AMS 作为中介(Broker)将广播分发给所有符合条件的接收者(Subscriber)。
  • 生命周期悖论 :Receiver 的 onReceive() 方法执行完毕后,系统会立即销毁 Receiver 实例。它不能执行耗时操作,否则会阻塞主线程导致 ANR。
  • 注册鸿沟静态注册 (Manifest)由系统唤醒,即使应用未运行也能接收;动态注册(Context.registerReceiver)依附于组件生命周期,组件销毁后失效。

2. BroadcastReceiver 的两种注册范式

2.1 静态注册(Manifest-declared)

AndroidManifest.xml 中声明 Receiver。这是系统级的注册方式。

学术定义

  • 常驻性:Receiver 信息在应用安装时由 PMS(PackageManagerService)解析并注册到系统中。
  • 唤醒能力:即使应用进程未运行,当广播到达时,AMS 会通过 Zygote 启动应用进程并实例化 Receiver。
  • 权限控制 :可以通过 android:permission 限制谁能发送广播给我。

Manifest 示例

xml 复制代码
<receiver
    android:name=".BootCompletedReceiver"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
    </intent-filter>
</receiver>

2.2 动态注册(Context-registered)

在代码中通过 Context.registerReceiver() 注册。这是应用级的注册方式。

学术定义

  • 临时性:Receiver 的生命周期与注册它的 Context(通常是 Activity 或 Service)绑定。
  • 灵活性:可以随时注册和注销,适合监听与 UI 状态相关的广播。
  • 无唤醒能力:如果应用进程已死,动态注册的 Receiver 无法接收广播。

代码示例(系统规范)

java 复制代码
public class MainActivity extends Activity {
    private MyReceiver receiver;

    @Override
    protected void onStart() {
        super.onStart();
        IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(receiver, filter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(receiver); // 必须注销,否则内存泄漏
    }
}

2.3 两种注册方式的对比

维度 静态注册 动态注册
注册地点 AndroidManifest.xml Java/Kotlin 代码
生命周期 独立于应用进程 依附于 Context
唤醒能力 (冷启动应用) 不能
执行时机 系统随时可能唤醒 仅在应用运行时
适用场景 系统事件(开机、电量、网络) 应用内事件(UI 更新)

3. 广播的分类与分发算法

3.1 普通广播(Normal Broadcast)

异步发送,所有 Receiver 同时接收,无法被拦截或修改。

学术定义

  • 并行分发:AMS 将广播发送给所有符合条件的 Receiver,不保证顺序。
  • 不可控:发送者无法知道 Receiver 是否接收成功,也无法修改数据。

3.2 有序广播(Ordered Broadcast)

同步发送,Receiver 按优先级依次接收,可以拦截或修改数据。

学术定义

  • 串行分发 :Receiver 按 android:priority 从高到低依次执行。
  • 拦截机制 :高优先级的 Receiver 可以调用 abortBroadcast() 阻止广播继续向下传递。
  • 数据修改 :Receiver 可以通过 setResultExtras() 修改数据,供下一个 Receiver 使用。

Manifest 示例

xml 复制代码
<receiver android:name=".HighPriorityReceiver">
    <intent-filter android:priority="1000">
        <action android:name="com.example.MY_ORDERED_BROADCAST" />
    </intent-filter>
</receiver>
<receiver android:name=".LowPriorityReceiver">
    <intent-filter android:priority="-1000">
        <action android:name="com.example.MY_ORDERED_BROADCAST" />
    </intent-filter>
</receiver>

3.3 粘性广播(Sticky Broadcast)【已废弃】

Android 5.0 (L) 起废弃。它允许广播"粘"在系统上,后来注册的 Receiver 也能收到之前的广播。

学术定义

  • 历史缓存:AMS 会缓存最后一条粘性广播。
  • 安全风险:任何应用都可以接收敏感信息,已被禁止。

4. 广播的分发流程(AMS 源码级解析)

4.1 广播发送流程

接收者进程 BroadcastQueue ActivityManagerService 发送者进程 接收者进程 BroadcastQueue ActivityManagerService 发送者进程 #mermaid-svg-BuRuN4GJdqFowPhr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BuRuN4GJdqFowPhr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BuRuN4GJdqFowPhr .error-icon{fill:#552222;}#mermaid-svg-BuRuN4GJdqFowPhr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BuRuN4GJdqFowPhr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BuRuN4GJdqFowPhr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BuRuN4GJdqFowPhr .marker.cross{stroke:#333333;}#mermaid-svg-BuRuN4GJdqFowPhr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BuRuN4GJdqFowPhr p{margin:0;}#mermaid-svg-BuRuN4GJdqFowPhr .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BuRuN4GJdqFowPhr text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BuRuN4GJdqFowPhr .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-BuRuN4GJdqFowPhr .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr .sequenceNumber{fill:white;}#mermaid-svg-BuRuN4GJdqFowPhr #sequencenumber{fill:#333;}#mermaid-svg-BuRuN4GJdqFowPhr #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-BuRuN4GJdqFowPhr .messageText{fill:#333;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BuRuN4GJdqFowPhr .labelText,#mermaid-svg-BuRuN4GJdqFowPhr .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .loopText,#mermaid-svg-BuRuN4GJdqFowPhr .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BuRuN4GJdqFowPhr .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-BuRuN4GJdqFowPhr .noteText,#mermaid-svg-BuRuN4GJdqFowPhr .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-BuRuN4GJdqFowPhr .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BuRuN4GJdqFowPhr .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BuRuN4GJdqFowPhr .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BuRuN4GJdqFowPhr .actorPopupMenu{position:absolute;}#mermaid-svg-BuRuN4GJdqFowPhr .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-BuRuN4GJdqFowPhr .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BuRuN4GJdqFowPhr .actor-man circle,#mermaid-svg-BuRuN4GJdqFowPhr line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-BuRuN4GJdqFowPhr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} sendBroadcast(intent)检查权限 (checkBroadcastFromSystem)enqueueParallelBroadcastLocked() / enqueueOrderedBroadcastLocked()构建 BroadcastRecordscheduleReceiver() (Binder IPC)实例化 ReceiveronReceive()finishReceiver()

4.2 BroadcastQueue 的内部机制

AMS 中有两个 BroadcastQueue:

  1. Foreground Broadcast Queue:前台广播,优先级高,超时时间短(10秒)。
  2. Background Broadcast Queue:后台广播,优先级低,超时时间长(60秒)。

源码解析

java 复制代码
// ActivityManagerService.java
public void broadcastIntent(...) {
    // 1. 检查调用者权限
    enforceNotIsolatedCaller("broadcastIntent");

    // 2. 处理粘性广播
    if (sticky) {
        // ...
    }

    // 3. 选择队列
    BroadcastQueue queue = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0
            ? mFgBroadcastQueue : mBgBroadcastQueue;

    // 4. 入队
    queue.enqueueOrderedBroadcastLocked(r);
    queue.scheduleBroadcastsLocked();
}

4.3 BroadcastRecord 数据结构

java 复制代码
// com.android.server.am.BroadcastRecord
class BroadcastRecord extends Binder {
    final Intent intent;                 // 广播 Intent
    final ProcessRecord callerApp;       // 发送者进程
    final String[] requiredPermissions;  // 接收者需要的权限
    final int receiversSize;             // 接收者数量
    final ArrayList<BroadcastFilter> receivers; // 动态接收者
    final ResolveInfo[] resolveInfo;     // 静态接收者
    int nextReceiver;                    // 下一个要处理的接收者索引(有序广播用)
    boolean ordered;                    // 是否是有序广播
}

5. 广播的安全性机制

5.1 发送权限(Send Permission)

发送广播时可以指定权限,只有拥有该权限的应用才能接收。

java 复制代码
// 发送者
sendBroadcast(intent, "com.example.permission.MY_PERMISSION");

// 接收者 Manifest
<uses-permission android:name="com.example.permission.MY_PERMISSION" />

5.2 接收权限(Receive Permission)

在 Manifest 中声明 Receiver 时指定权限,只有拥有该权限的发送者才能发送广播给我。

xml 复制代码
<receiver
    android:name=".MyReceiver"
    android:permission="com.example.permission.ANOTHER_PERMISSION">
    <intent-filter>
        <action android:name="com.example.MY_ACTION" />
    </intent-filter>
</receiver>

5.3 LocalBroadcastManager(进程内安全广播)

LocalBroadcastManager 是 Support Library 提供的进程内广播 工具。它不使用 Binder IPC,也不通过 AMS,而是直接在应用进程内分发。

学术定义

  • 安全性:广播数据不会离开应用进程,不会被其他应用截获。
  • 效率:避免了 IPC 开销,速度更快。
  • 现状 :已被 LiveDataRxJava 等响应式编程库取代,但在理解广播机制时仍具学术价值。

6. 广播的陷阱与系统限制

6.1 ANR 风险

onReceive() 运行在主线程,且只有 10 秒 (前台广播) 或 60 秒(后台广播)的执行时间。

学术定义

  • ANR(Application Not Responding) :如果 onReceive() 执行超过规定时间,系统会弹出 ANR 对话框。
  • 禁忌绝对不能在 onReceive() 中执行网络请求、数据库读写、文件 IO 或睡眠操作

6.2 广播延迟与排队

当系统繁忙时,广播可能会被延迟。特别是在 Android 8.0 (O) 之后,系统对后台广播进行了严格限制。

Android 8.0+ 的限制

  • 后台执行限制:应用在后台时,无法注册隐式广播的静态 Receiver(部分系统广播除外,如开机广播)。
  • 原因:防止应用滥用广播在后台唤醒,消耗电量。

6.3 替代方案

由于广播的局限性,现代 Android 开发推荐使用以下方案:

场景 推荐方案 学术理由
应用内通信 LiveData / RxJava / Flow 响应式编程,解耦更彻底,无 IPC 开销。
跨进程通信 Messenger / AIDL 需要双向通信或复杂数据结构时。
后台任务 WorkManager / JobScheduler 系统调度,省电,符合后台限制。
系统事件 静态注册广播 唯一合法场景,如开机、电量变化。

7. 关键源码解析:广播的匹配算法

AMS 如何找到符合条件的 Receiver?

java 复制代码
// PackageManagerService.java
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
    // 1. 查询静态 Receiver (Manifest)
    List<ResolveInfo> receivers = mReceivers.queryIntent(intent, resolvedType, flags, userId);

    // 2. 过滤权限
    for (ResolveInfo ri : receivers) {
        if (!grantImplicitAccess(userId, ri, uid, owningUid, false)) {
            continue; // 权限不足,过滤掉
        }
    }
    return receivers;
}

8. 广播的冷启动效应与性能影响

8.1 冷启动炸弹(Cold Start Bomb)

静态注册的 Receiver 具有唤醒能力,这意味着一个广播可以启动一个已经死亡的应用进程。

学术定义

  • 进程创建开销:每次唤醒都需要 Zygote fork 新进程,加载 ART 虚拟机,初始化 Application。
  • 累积效应:如果多个应用都监听同一个高频广播(如网络变化),会导致系统频繁创建和销毁进程,造成严重的性能损耗和电量消耗。

8.2 广播的序列化执行

即使是普通广播,在同一个应用进程中,Receiver 的 onReceive() 也是串行执行的。如果一个 Receiver 执行时间过长,会阻塞后续 Receiver 的执行。


9. 关键数据结构与源码定义(续)

9.1 BroadcastFilter(动态注册记录)

java 复制代码
// com.android.server.am.BroadcastFilter
class BroadcastFilter extends IntentFilter {
    final String packageName;
    final String receiverClassName;
    final int owningUid;
    final int owningPid;
    final boolean instantApp;
}

9.2 ReceiverList(Receiver 列表)

java 复制代码
// com.android.server.am.ReceiverList
class ReceiverList extends ArrayList<BroadcastFilter> {
    final ProcessRecord app;            // 所属进程
    final int pid;                     // 进程 ID
    final int uid;                     // 用户 ID
    final String packageName;          // 包名
}

10. 本篇总结(Knowledge Closure)

关键点 纯学术定义
Receiver 的本质 由 AMS 管理的事件监听器,生命周期极短,运行在主线程。
注册范式 静态注册(系统级,可唤醒)与动态注册(应用级,依附生命周期)。
分发模型 异步 Pub-Sub 模型,有序广播提供拦截和修改能力。
安全性 通过发送权限和接收权限实现双向保护。
系统限制 Android 8.0+ 严格限制后台静态广播,防止滥用。
性能陷阱 冷启动开销大,执行时间长会导致 ANR。

下一篇预告第二板块:Android 四大组件标准化学理 | 第十篇:ContentProvider 数据共享与 SQLite 引擎

相关推荐
JohnnyDeng942 小时前
【Android】Room 数据库高级用法与性能调优:从查询瓶颈到毫秒级响应
android·性能优化·kotlin·room
zeqinjie2 小时前
Flutter 折叠屏 iPad / 宽屏适配实践
android·前端·flutter
ab_dg_dp2 小时前
Android 17+ 提取 AIDL 生成 Java 文件的实用脚本
android·java·python
Arrom3 小时前
DLNA 渲染端排障实战:从 20s 卡顿到 stale subscriber 的两周追凶之旅
android·java
_李小白3 小时前
【android opencv学习笔记】Day 32:直线检测之霍夫变换
android·opencv·学习
plainGeekDev7 小时前
文件读写(Java IO)→ Kotlin 扩展函数
android·java·kotlin
s_nshine8 小时前
释放C盘,迁移studio相关数据到其他盘
android·windows·android studio·内存·c盘
毛豆的毛豆Y8 小时前
新上架!给 Gitee 用户做了个工具:CopoGit
gitee
韩曙亮8 小时前
【Flutter】Flutter 中的 Android / iOS 特殊配置 ① ( 网络权限配置 | HTTP 明文传输配置 | 应用名称配置 )
android·网络·flutter·http·ios·网络权限