【Android FrameWork】第二十六天:BroadcastReceiver

BroadcastReceiver

BroadcastReceiver(广播接收器)是 Android 四大组件中负责 "事件通知" 的核心组件,其核心价值在于实现跨进程、跨组件的解耦通信(如系统启动完成通知、网络状态变化、应用安装卸载等)。

理解其注册机制与跨进程分发原理,是掌握 Android 组件通信与系统服务交互的关键。

本文将从注册方式拆解、跨进程架构、分发流程三个层面,结合源码关键逻辑,全面解析其底层机制。

核心概念与角色定位

在分析原理前,需明确参与广播通信的三大核心角色,以及广播的分类特征:

1. 核心角色

角色 职责 核心组件 / 服务
广播发送者 发起广播(如应用、系统服务) Context.sendBroadcast()Intent
广播接收者 接收并处理广播(自定义 BroadcastReceiver 子类) BroadcastReceiver.onReceive()
系统调度中心 管理接收者注册、匹配广播、跨进程分发 AMS(ActivityManagerService)PMS(PackageManagerService)

2. 广播分类(按分发特性)

类型 特点 典型场景
普通广播(Normal) 异步分发、无优先级、无法中断 应用内通知、非关键系统事件
有序广播(Ordered) 同步分发、按优先级排序、可中断 系统权限验证(如开机启动权限)
粘性广播(Sticky) 广播发送后留存,新注册接收者可接收 系统状态持久通知(如电池电量)
本地广播(Local) 仅本应用内分发、无跨进程能力 应用内组件通信(如 Activity→Service)

BroadcastReceiver 注册机制

BroadcastReceiver 的注册分为静态注册 (Manifest 声明)和动态注册(代码调用),二者的核心区别在于 "注册时机" 与 "依赖的系统服务" 不同,最终均需将接收者信息注册到 AMS 中统一管理。

1. 静态注册:PMS 解析 + AMS 存储

静态注册是在AndroidManifest.xml中声明接收者,由系统在应用安装时解析,适用于 "需接收广播但应用未启动" 的场景(如接收开机广播)。

注册流程(核心四步)
graph TD A[应用安装] --> B[PMS解析Manifest] B --> C[提取标签信息] C --> D[存储到PMS组件数据库] D --> E[应用首次启动时,AMS从PMS同步接收者信息]
关键细节
  • Manifest 声明格式 :需指定action(广播匹配标识)、permission(接收权限,可选)、enabled(是否启用)等属性:

  • PMS 的作用 :应用安装时,PMS 通过PackageParser.parsePackage()解析AndroidManifest.xml,将<receiver>标签对应的组件信息(类名、action、权限)存储到PackageInfo中,再写入系统数据库(/data/system/packages.xml)。

  • AMS 的同步 :当应用首次启动(或系统重启)时,AMS 通过PMS.getPackageInfo()获取该应用的静态接收者信息,创建ReceiverInfo对象(包含接收者类名、匹配规则、权限),并存储到 AMS 内部的 "接收者注册表"(mReceiverResolver,一个 IntentFilter 匹配器)。

2. 动态注册:Context→AMS 直接注册

动态注册是在代码中通过Context.registerReceiver()主动注册,适用于 "仅在应用运行时需要接收广播" 的场景(如监听网络变化),需在组件销毁时调用unregisterReceiver()注销,避免内存泄漏。

注册流程(核心五步,基于 Android 13 源码)

应用(如Activity) ContextImpl AMS registerReceiver(receiver, filter) Binder调用 registerReceiver()(传递Receiver、Filter、权限) 验证权限(如接收者是否有权限、是否为系统广播) 创建IReceiverProxy(Binder代理,用于跨进程回调) 将ReceiverInfo+IReceiverProxy存入mReceiverResolver 应用(如Activity) ContextImpl AMS

关键细节
  • 核心代码示例
java 复制代码
// 动态注册网络变化广播

MyDynamicReceiver receiver = new MyDynamicReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY\_ACTION); // 网络变化action
// 注册(ContextImpl的registerReceiver方法)
registerReceiver(receiver, filter);

// 组件销毁时注销(必须!)
@Override
protected void onDestroy() {
   super.onDestroy();
   unregisterReceiver(receiver);
}
  • Binder 代理的作用 :动态注册时,AMS 不会直接持有接收者对象(跨进程无法持有),而是创建IReceiverProxy(Binder 代理对象),存储在 AMS 的接收者注册表中。当有匹配广播时,AMS 通过IReceiverProxyperformReceive()方法,跨进程回调接收者的onReceive()

  • 生命周期绑定 :动态注册的接收者与注册时的Context生命周期绑定(如 Activity 注册则随 Activity 销毁而失效),若未注销,Context会被 AMS 的IReceiverProxy引用,导致内存泄漏。

3. 两种注册方式的核心差异

维度 静态注册 动态注册
注册时机 应用安装时(PMS 解析) 应用运行时(代码调用)
依赖服务 PMS(解析存储)+ AMS(同步管理) 直接依赖 AMS
进程启动逻辑 接收广播时 AMS 自动启动进程 需进程已运行(否则无法注册)
灵活性 固定配置(Manifest 不可动态修改) 可动态添加 / 移除 action、权限
Android 8.0 + 限制 无法接收隐式广播(需指定包名) 无隐式广播限制

跨进程分发原理

Android 中广播的跨进程分发,本质是以 AMS 为核心,通过 Binder 通信实现 "发送者→AMS→接收者" 的双向跨进程调用。以下以 "普通广播" 为例,拆解完整分发流程(有序广播、粘性广播仅在过滤和分发阶段有差异)。

1. 整体架构:三大角色的跨进程交互

复制代码
┌───────────────┐     Binder      ┌───────────────┐     Binder      ┌───────────────┐

│ 广播发送者进程 │>│     AMS       ││ 接收者进程    │

│(如应用、系统)│                 │(调度中心)   │                │(静态/动态)  │

└───────────────┘                 └───────────────┘                └───────────────┘
  • 发送者→AMS :发送者调用sendBroadcast(),通过 Binder 将Intent(包含 action、数据、类型)发送到 AMS 的broadcastIntent()方法。

  • AMS→接收者 :AMS 匹配到接收者后,通过 Binder 通知接收者进程(静态注册需先启动进程,动态注册直接回调),触发onReceive()

2. 详细分发流程(普通广播,分 6 步)

步骤 1:发送者发起广播

应用或系统服务调用Context.sendBroadcast(Intent intent),最终通过ContextImplsendBroadcast()方法,将Intent封装为BroadcastIntentData,通过 Binder 跨进程发送到 AMS。

关键代码(ContextImpl):

java 复制代码
@Override
public void sendBroadcast(Intent intent) {
   warnIfCallingFromSystemProcess();
   // 调用AMS的broadcastIntent方法(Binder跨进程)
   ActivityManager.getService().broadcastIntent(
       mMainThread.getApplicationThread(), intent, null, null,
       0, null, null, null, false, false, 0);

}
步骤 2:AMS 接收广播并验证

AMS 的broadcastIntent()方法首先进行合法性验证,过滤无效广播:

  1. 权限验证 :若发送者指定了sendPermission,验证发送者是否有该权限;

  2. 广播类型验证 :如粘性广播需android.permission.BROADCAST_STICKY权限,Android 12 后已废弃大部分粘性广播;

  3. 隐式广播限制 :Android 8.0 + 对静态注册接收者,仅允许接收 "显式广播"(指定ComponentName)或系统白名单广播(如BOOT_COMPLETED)。

步骤 3:AMS 匹配接收者(核心:IntentFilter 匹配)

AMS 通过内部的mReceiverResolverIntentResolver类型,基于 action、category、data 类型构建的匹配树),从 "接收者注册表" 中筛选出符合条件的接收者:

  1. action 匹配 :接收者的IntentFilter必须包含广播Intentaction

  2. category 匹配 :若广播Intent包含category,接收者的IntentFilter必须包含所有category

  3. data 匹配 :若广播Intent指定了data(如content://),接收者需配置对应的dataScheme/dataType

  4. 权限匹配 :若接收者注册时指定了permission,发送者必须拥有该权限才能向其分发。

匹配结果:AMS 会将符合条件的接收者按类型分类(普通 / 有序 / 粘性),存储到BroadcastQueue(AMS 的广播队列,分前台 / 后台队列,前台优先级高)。

步骤 4:AMS 处理接收者进程状态(静态注册专属)
  • 动态注册接收者:若接收者进程已运行(动态注册时进程必然存活),直接进入下一步分发;

  • 静态注册接收者 :若接收者进程未启动,AMS 会通过startProcessLocked()方法启动该进程(基于接收者的PackageNameClassName),进程启动后会自动将静态接收者信息同步到 AMS,再进入分发。

步骤 5:AMS 通过 Binder 分发广播到接收者

AMS 的BroadcastQueue会遍历匹配到的接收者列表,通过IReceiverProxy(动态注册)或ReceiverDispatcher(静态注册)发起 Binder 跨进程调用:

  • 动态注册 :AMS 调用IReceiverProxy.performReceive(),直接回调接收者进程中BroadcastReceiveronReceive()方法;

  • 静态注册 :AMS 通过ActivityThreadscheduleReceiver()方法,将广播发送到接收者进程的主线程消息队列,主线程Looper处理消息时调用onReceive()

关键注意点:onReceive()运行在主线程(系统进程的接收者可能在其他线程),执行时间不能超过 10 秒,否则会触发 ANR(Application Not Responding)。

步骤 6:接收者处理广播

接收者在onReceive(Context context, Intent intent)中处理广播逻辑,如更新 UI、启动 Service 等。处理完成后:

  • 动态注册接收者:保持注册状态,等待下一次广播;

  • 静态注册接收者:若进程仅为接收广播启动,且无其他组件运行,AMS 会在短时间内回收该进程(节省内存)。

3. 有序广播的特殊分发逻辑

与普通广播相比,有序广播的核心差异在 "同步分发" 和 "优先级控制":

  1. 优先级排序 :AMS 按接收者注册时的priority(-1000~1000,默认 0)从高到低排序,优先级相同则静态注册在前;

  2. 同步分发 :AMS 先向优先级最高的接收者分发广播,待其onReceive()执行完成后,再向下一个接收者分发;

  3. 中断机制 :接收者可调用abortBroadcast()中断广播,后续优先级低的接收者将无法收到。

关键特性与实践中的核心问题

1. 权限控制:双向权限校验

为防止广播被恶意发送或接收,Android 提供双向权限控制:

  • 发送者权限 :发送广播时指定sendPermission(如Context.sendBroadcast(intent, "com.example.PERMISSION_SEND")),接收者必须在 Manifest 中声明该权限才能接收;

  • 接收者权限 :接收者注册时指定permission(如:permission="com.example.PERMISSION_RECEIVE">),发送者必须声明该权限才能向其发送广播。

2. Android 版本兼容性问题(高频坑点)

Android 版本 关键限制 解决方案
8.0(API 26) 静态注册接收者无法接收隐式广播 1. 发送显式广播(指定 ComponentName);2. 改用动态注册;3. 若为系统广播,加入白名单
12(API 31) 禁止发送 / 接收大部分粘性广播 改用WorkManager或本地广播替代
13(API 33) 接收POST_NOTIFICATIONS等广播需申请运行时权限 在代码中请求Manifest.permission.POST_NOTIFICATIONS权限

3. 常见问题定位思路

  • 静态注册接收者收不到广播
  1. 检查是否为 Android 8.0 + 隐式广播限制,尝试发送显式广播;

  2. 检查enabledexported属性是否为true

  3. 检查广播是否在系统白名单(如BOOT_COMPLETED需申请RECEIVE_BOOT_COMPLETED权限)。

  • 动态注册接收者内存泄漏
  1. 确保在onDestroy()中调用unregisterReceiver()

  2. 若在 Service 中注册,需在onDestroy()onStop()中注销。

  • 广播分发延迟
  1. 检查接收者是否在后台队列(可改用sendBroadcastAsUser()指定前台队列);

  2. 避免在onReceive()中执行耗时操作(需启动 Service 处理)。

总结

BroadcastReceiver 的注册与跨进程分发,本质是以 AMS 为中央调度中心,通过 Binder 实现跨进程通信,结合 PMS 完成组件信息管理的机制:

  1. 注册 :静态注册靠 PMS 解析 Manifest,动态注册靠 Context 直连 AMS,最终均将接收者信息存入 AMS 的mReceiverResolver

  2. 分发 :发送者通过 Binder 将广播提交给 AMS,AMS 按规则匹配接收者,再通过 Binder 跨进程通知接收者进程,触发onReceive()

  3. 关键保障:权限校验防止恶意通信,队列调度保障分发优先级,生命周期绑定避免内存泄漏。

相关推荐
有位神秘人14 小时前
Android中Notification的使用详解
android·java·javascript
·云扬·14 小时前
MySQL Binlog落盘机制深度解析:性能与安全性的平衡艺术
android·mysql·adb
独自破碎E15 小时前
【BISHI9】田忌赛马
android·java·开发语言
代码s贝多芬的音符16 小时前
android 两个人脸对比 mlkit
android
darkb1rd18 小时前
五、PHP类型转换与类型安全
android·安全·php
gjxDaniel19 小时前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
csj5019 小时前
安卓基础之《(22)—高级控件(4)碎片Fragment》
android
峥嵘life19 小时前
Android16 【CTS】CtsMediaCodecTestCases等一些列Media测试存在Failed项
android·linux·学习
stevenzqzq20 小时前
Compose 中的状态可变性体系
android·compose
似霰20 小时前
Linux timerfd 的基本使用
android·linux·c++