【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. 关键保障:权限校验防止恶意通信,队列调度保障分发优先级,生命周期绑定避免内存泄漏。

相关推荐
@#---9 小时前
如何准确判断json文件并且拿到我想要的信息
android·python·json
程序员陆业聪11 小时前
Android插件化原理与方案详解
android
惟恋惜12 小时前
Jetpack Compose 界面元素状态(UI Element State)详解
android·ui·android jetpack
_李小白13 小时前
【Android FrameWork】延伸阅读:IGraphicBufferProducer驱动UI绘制过程
android·ui
_李小白14 小时前
【Android FrameWork】第二十八天:Activity 的 UI 绘制全过程
android·ui
_李小白15 小时前
【Android FrameWork】第三十天:Surface创建流程解析
android
元亓亓亓15 小时前
考研408--操作系统--day8--操作系统--虚拟内存&请求分页&页面置换/分配
android·java·开发语言·虚拟内存
有位神秘人16 小时前
Android的Compose系列之文本TextView
android
Engineer-Jsp16 小时前
Flutter 开发 Android 原生开发神器 flutter_api_stub
android·flutter