文章内容
- 一、一次广播的两个入口:注册与发送
- 1.1 示例里的三个角色与一条时间线
- 1.2 全局概览:谁持有注册表,谁持有队列
- 二、动态注册:接收者怎样进入 AMS
- 2.1 客户端准备跨进程回调对象
- 2.2 AMS 侧的两张表
- 2.3 ReceiverList 与 BroadcastFilter 保存的内容
- 三、发送广播:AMS 里的收集与入队
- 3.1 从 broadcastIntent 到 broadcastIntentLocked
- 3.2 两条接收者收集路径
- 3.3 两个队列及合并顺序
- 3.4 FLAGRECEIVERREGISTEREDONLY 改变收集范围
- 四、BroadcastQueue 的投递循环
- 4.1 从调度消息进入处理循环
- 4.2 并行队列先投完
- 4.3 动态接收者的两条投递分支
- 4.4 Args 把回调送到主线程
- 4.5 finishReceiver 推进有序队列
- 五、静态注册:从 PMS 解析到进程创建
- 5.1 安装期解析 manifest
- 5.2 发送期由 PMS 查询静态组件
- 5.3 进程状态决定直接投递或挂起
- 5.4 进程 attach
- 5.5 应用进程反射创建接收者
- 六、某些真机为何收不到静态广播
- 6.1 ROM 改动影响的是进程创建环节
- 七、小结
- 7.1 动态与静态是两条收集路径
- 7.2 BroadcastReceiver 不提供 Context 能力
- 7.3 有序广播的串行由队列推进
- 7.4 mPendingBroadcast 对应待启动进程的当前记录
一次广播从应用发出后,系统怎样找到接收者,又怎样把调用送回 onReceive?动态注册和 manifest 静态声明使用不同的收集路径,却会在 AMS 的分发阶段汇合。本文沿注册、入队、投递与完成回调追踪对象和状态的变化,并说明静态接收者为何依赖目标进程的创建与 attach。全文源码以 AOSP android-6.0.0_r1 为基准。
一、一次广播的两个入口:注册与发送
1.1 示例里的三个角色与一条时间线
示例由发送方 MessageService、动态接收方 MainActivity 和静态接收方 TestBroadcastReceiver 组成。MainActivity 绑定服务并注册匿名接收者;服务周期性发送携带时间戳的广播;另一个应用通过 manifest 声明接收者,用它观察目标进程尚未运行时的投递行为。
动态接收端的结构很简单:应用保存普通的 BroadcastReceiver 对象,再通过 Context 完成注册和反注册。
// 定义匿名接收者private final BroadcastReceiver mMusicBr = new BroadcastReceiver() {// 覆盖广播回调 @Override// 接收系统传入的 Context 和 Intent public void onReceive(Context context, Intent intent) {// 记录广播携带的时间值 Log.d(TAG, "msg time=" + intent.getIntExtra("time", 0));// 结束回调方法 }// 结束匿名对象};// 绑定消息服务bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);// 按服务定义的 action 构造过滤器IntentFilter mFilter = new IntentFilter(MESSAGE_SERVICE_FILTER);// 注册动态接收者registerReceiver(mMusicBr, mFilter);// 反注册动态接收者unregisterReceiver(mMusicBr);发送端每次构造带 action 的 Intent,填入时间值后交给 sendBroadcast。
// 启动工作线程new Thread(new Runnable() {// 执行线程任务 @Override// 定义运行入口 public void run() {// 持续发送时间广播 while (true) {// 控制发送间隔 Thread.sleep(1000);// 用常量指定 action Intent intent = new Intent(MESSAGE_SERVICE_FILTER);// 放入时间值 intent.putExtra("time", time);// 发出无序广播 sendBroadcast(intent);// 结束循环体 }// 结束运行方法 }// 结束 Runnable}).start();静态接收者的入口来自 manifest。这里只保留声明结构本身,action 的具体字符串与发送方保持一致即可。
<!-- 声明接收者类 --><receiver android:name="com.example.myapplication1.TestBroadcastReceiver"><!-- 声明接收规则 --> <intent-filter><!-- 声明目标 action --> <action android:name="MESSAGE_SERVICE_FILTER" /><!-- 结束接收规则 --> </intent-filter><!-- 结束接收者声明 --></receiver>接收器实例收到广播后记录日志并启动 MainActivity,这个可观察动作可用于判断接收进程是否已被拉起。
// 覆盖静态接收器回调@Override// 接收系统提供的受限 Contextpublic void onReceive(Context context, Intent intent) {// 记录接收日志 Log.e(TAG, "onReceive");// 构造目标 Activity 的 Intent Intent activityIntent = new Intent(context, MainActivity.class);// 启动目标 Activity context.startActivity(activityIntent);// 结束回调方法}1.2 全局概览:谁持有注册表,谁持有队列
应用进程持有具体的 BroadcastReceiver 实例;system_server 中的 Activity Manager Service(AMS)持有动态注册信息和 BroadcastQueue;Package Manager Service(PMS)保存安装期解析出的静态组件。发送发生后,AMS 分别从内存注册表和 PMS 收集目标,随后交给广播队列。接收者位于其他进程时,跨进程边界传递的是 Binder 接口及序列化参数,而非接收者 Java 对象本身。

二、动态注册:接收者怎样进入 AMS
2.1 客户端准备跨进程回调对象
ContextImpl.registerReceiverInternal 位于注册链的应用进程一侧。它取得 IIntentReceiver,随后调用 IActivityManager.registerReceiver 进入 AMS(ContextImpl.java 第 1163~1190 行)。
// 定义注册内部方法private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,// 接收过滤器与广播权限 IntentFilter filter, String broadcastPermission,// 接收调度器与调用方上下文 Handler scheduler, Context context) {// 先声明跨进程回调对象 IIntentReceiver rd = null;// 传入接收者实例时才准备回调对象 if (receiver != null) {// 包信息与上下文齐全时走 LoadedApk 缓存 if (mPackageInfo != null && context != null) {// 未指定调度器时使用主线程 Handler if (scheduler == null) {// 取得主线程 Handler scheduler = mMainThread.getHandler();// 结束调度器兜底 }// 取得复用或新建的调度对象 rd = mPackageInfo.getReceiverDispatcher(// 传入接收者、上下文与调度器 receiver, context, scheduler,// 传入插桩对象并标记为已注册 mMainThread.getInstrumentation(), true);// 缺少包信息时直接构造调度对象 } else {// 未指定调度器时同样使用主线程 Handler if (scheduler == null) {// 取得主线程 Handler scheduler = mMainThread.getHandler();// 结束调度器兜底 }// 直接创建调度器并取出 Binder 接口 rd = new LoadedApk.ReceiverDispatcher(// 传入接收者、上下文、调度器与注册标志 receiver, context, scheduler, null, true).getIIntentReceiver();// 结束包信息判断 }// 结束接收者判断 }// 跨进程调用可能抛远端异常 try {// 进入 AMS 的注册接口 return ActivityManagerNative.getDefault().registerReceiver(// 传入应用线程与包名 mMainThread.getApplicationThread(), mBasePackageName,// 传入回调对象、过滤器、权限与用户 rd, filter, broadcastPermission, userId);// 远端异常时注册失败 } catch (RemoteException e) {// 返回空结果 return null;// 结束异常处理 }// 结束内部方法}LoadedApk.getReceiverDispatcher 位于普通对象与 Binder 回调之间。mReceivers 以 Context 找到内部 map,再按 BroadcastReceiver 复用 ReceiverDispatcher(LoadedApk.java 第 706~734 行)。
// 定义取得接收者调度器的方法public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,// 接收上下文与调度处理器 Context context, Handler handler,// 接收插桩对象与注册标志 Instrumentation instrumentation, boolean registered) {// 在接收者缓存上加锁 synchronized (mReceivers) {// 声明调度器变量 LoadedApk.ReceiverDispatcher rd = null;// 声明按上下文分组的二级映射 ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;// 已注册的接收者允许复用缓存 if (registered) {// 按上下文取出二级映射 map = mReceivers.get(context);// 映射存在时按接收者实例查找 if (map != null) {// 查找已有调度器 rd = map.get(r);// 结束映射判断 }// 结束复用判断 }// 缓存未命中时创建调度器 if (rd == null) {// 构造调度器并保存上下文、Handler 与插桩对象 rd = new ReceiverDispatcher(r, context, handler,// 传入插桩对象与注册标志 instrumentation, registered);// 注册类接收者需要写入缓存 if (registered) {// 该上下文首次注册时二级映射还不存在 if (map == null) {// 创建二级映射 map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();// 按上下文写回外层缓存 mReceivers.put(context, map);// 结束二级映射创建 }// 按接收者实例保存调度器 map.put(r, rd);// 结束缓存写入 }// 命中缓存时校验上下文与 Handler 是否一致 } else {// 复用前校验注册上下文 rd.validate(context, handler);// 结束缓存分支 }// 清除被反注册的标记 rd.mForgotten = false;// 返回可跨进程的 Binder 回调接口 return rd.getIIntentReceiver();// 结束同步块 }// 结束方法}InnerReceiver 是 IIntentReceiver.Stub 子类。AMS 回调它的 performReceive 后,调用才转给同一进程中的 ReceiverDispatcher(LoadedApk.java 第 788~825 行)。
// 跨进程回调落在 Stub 这一层final static class InnerReceiver extends IIntentReceiver.Stub {// 弱引用所属调度器 final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;// 强引用,注册接收者在存活期内持有调度器 final LoadedApk.ReceiverDispatcher mStrongRef; // 构造时保存调度器引用 InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {// 保存弱引用 mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);// 按参数决定是否保留强引用 mStrongRef = strong ? rd : null;// 结束构造方法 }// 定义 Binder 回调方法 public void performReceive(Intent intent, int resultCode, String data,// 接收 extras 与广播属性 Bundle extras, boolean ordered, boolean sticky, int sendingUser) {// 从弱引用取出调度器 LoadedApk.ReceiverDispatcher rd = mDispatcher.get();// 调度器还在时继续投递 if (rd != null) {// 转给进程内的调度器处理 rd.performReceive(intent, resultCode, data, extras,// 传入有序、sticky 与用户参数 ordered, sticky, sendingUser);// 调度器已被回收说明接收者已反注册 } else {// 取得 AMS 接口 IActivityManager mgr = ActivityManagerNative.getDefault();// 替接收者向 AMS 结束这次广播 try {// extras 存在时禁止携带文件描述符 if (extras != null) {// 关闭 fd 许可 extras.setAllowFds(false);// 结束 extras 判断 }// 通知 AMS 本次接收已处理完 mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());// 远端异常时只记录日志 } catch (RemoteException e) {// 记录结束失败 Slog.w(ActivityThread.TAG, "Couldn't finish broadcast to unregistered receiver");// 结束异常处理 }// 结束调度器判断 }// 结束回调方法 }// 结束内部类}2.2 AMS 侧的两张表
AMS 以 receiver.asBinder() 查询 mRegisteredReceivers。首次注册会建立 ReceiverList;每个 IntentFilter 随后包装成 BroadcastFilter,同时加入 ReceiverList 和 mReceiverResolver(ActivityManagerService.java 第 16223~16257 行)。
// 按 Binder 标识查找接收者列表ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());// 首次注册时创建列表if (rl == null) {// 用调用者进程、pid、uid 与回调对象构造列表 rl = new ReceiverList(this, callerApp, callingPid, callingUid,// 传入用户与跨进程回调对象 userId, receiver);// 调用者进程存在时把列表挂到进程记录上 if (rl.app != null) {// 进程记录维护自己的接收者列表 rl.app.receivers.add(rl);// 没有进程记录时改为监听回调对象的存活 } else {// 回调对象死亡时得到通知 try {// 注册 Binder 死亡回调 receiver.asBinder().linkToDeath(rl, 0);// 回调对象已失效时只返回 sticky 结果 } catch (RemoteException e) {// 结束注册流程 return sticky;// 结束异常处理 }// 标记已注册死亡监听 rl.linkedToDeath = true;// 结束进程记录判断 }// 把列表登记到 Binder 索引 mRegisteredReceivers.put(receiver.asBinder(), rl);// 同一回调对象重复注册时校验 uid} else if (rl.uid != callingUid) {// uid 不一致时拒绝注册 throw new IllegalArgumentException(// 报告 uid 冲突 "Receiver requested to register for uid " + callingUid// 附带原先记录的 uid + " was previously registered for uid " + rl.uid);// 再校验 pid} else if (rl.pid != callingPid) {// pid 不一致时拒绝注册 throw new IllegalArgumentException(// 报告 pid 冲突 "Receiver requested to register for pid " + callingPid// 附带原先记录的 pid + " was previously registered for pid " + rl.pid);// 再校验 userId} else if (rl.userId != userId) {// 用户不一致时拒绝注册 throw new IllegalArgumentException(// 报告用户冲突 "Receiver requested to register for user " + userId// 附带原先记录的用户 + " was previously registered for user " + rl.userId);// 结束首次创建分支}// 用过滤条件和所属列表创建 BroadcastFilterBroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,// 保存权限、uid 与用户 permission, callingUid, userId);// 把过滤器挂到接收者列表rl.add(bf);// 把过滤器加入动态注册索引mReceiverResolver.addFilter(bf);反注册会同时清理两种索引,避免 Binder 表与 intent 过滤索引残留不同步(ActivityManagerService.java 第 16325~16329 行)。
// 定义接收者移除方法void removeReceiverLocked(ReceiverList rl) {// 从 Binder 索引移除列表 mRegisteredReceivers.remove(rl.receiver.asBinder());// 逆序遍历列表中的过滤器 for (int i = rl.size() - 1; i >= 0; i--) {// 从 IntentResolver 移除过滤器 mReceiverResolver.removeFilter(rl.get(i));// 结束遍历 }// 结束移除方法}
2.3 ReceiverList 与 BroadcastFilter 保存的内容
ReceiverList 继承 ArrayList<BroadcastFilter>,持有 IIntentReceiver、所属进程、pid、uid、userId 以及当前有序广播。BroadcastFilter 继承 IntentFilter,反向引用 ReceiverList,并补充包名、权限和所有者身份。前者回答“回调哪个 Binder、属于哪个进程”,后者回答“哪些 Intent 能匹配、以什么身份接收”。
因此,动态注册的结果不只是一次远程调用。AMS 获得了一组可按 Binder 管理、可按 Intent 条件检索的常驻内存对象。
BroadcastRecord 的 receivers 字段注释写明它同时容纳 BroadcastFilter 与 ResolveInfo(BroadcastRecord.java 第 58 行)。同一条广播记录的接收者列表里混装着这两种对象:动态注册者是 BroadcastFilter,静态解析结果是 ResolveInfo;下游按类型分流,这也是第四节与第五节要反复用到的分界。
三、发送广播:AMS 里的收集与入队
3.1 从 broadcastIntent 到 broadcastIntentLocked
发送方经 ContextImpl.sendBroadcast 和 ActivityManagerNative 进入 AMS。公开 Binder 方法先校验 Intent,再在 AMS 锁内调用 broadcastIntentLocked(ActivityManagerService.java 第 16925~16946 行)。
// 定义 AMS 的广播入口public final int broadcastIntent(IApplicationThread caller,// 接收 Intent、类型与结果接收端 Intent intent, String resolvedType, IIntentReceiver resultTo,// 接收结果参数 int resultCode, String resultData, Bundle resultExtras,// 接收权限、appOp 与 options String[] requiredPermissions, int appOp, Bundle options,// 接收串行、sticky 与用户参数 boolean serialized, boolean sticky, int userId) {// 拒绝 isolated 进程调用 enforceNotIsolatedCaller("broadcastIntent");// 在 AMS 锁内处理 synchronized(this) {// 校验并规范化广播 Intent intent = verifyBroadcastLocked(intent);// 查找调用者进程记录 final ProcessRecord callerApp = getRecordForAppLocked(caller);// 记录调用方 pid final int callingPid = Binder.getCallingPid();// 记录调用方 uid final int callingUid = Binder.getCallingUid();// 清除调用身份并保存原值 final long origId = Binder.clearCallingIdentity();// 进入锁内的广播实现 int res = broadcastIntentLocked(callerApp,// 传入调用包名 callerApp != null ? callerApp.info.packageName : null,// 传入广播与结果参数 intent, resolvedType, resultTo, resultCode, resultData, resultExtras,// 传入权限与广播属性 requiredPermissions, appOp, null, serialized, sticky,// 传入调用身份与用户 callingPid, callingUid, userId);// 恢复调用身份 Binder.restoreCallingIdentity(origId);// 返回处理结果 return res;// 结束同步块 }// 结束入口方法}3.2 两条接收者收集路径
静态组件由 collectReceiverComponents 查询;动态接收者由 mReceiverResolver.queryIntent 查询。FLAG_RECEIVER_REGISTERED_ONLY 置位时,静态查询分支不会执行(ActivityManagerService.java 第 16744~16773 行)。下面这一段是主干:userId 为 USER_ALL 且调用方是 shell 时,代码会逐用户查询并跳过禁止调试的用户,该分支在这里没有展开。
// 初始化静态接收者列表List receivers = null;// 初始化动态接收者列表List<BroadcastFilter> registeredReceivers = null;// 检查是否限制为动态接收者if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)// 标志未置位时查询静态组件 == 0) {// 向 PMS 收集静态接收者 receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);// 结束静态查询条件}// 隐式 Intent 才查询动态过滤器if (intent.getComponent() == null) {// 从 AMS 的解析器查询动态接收者 registeredReceivers = mReceiverResolver.queryIntent(intent,// 传入类型、默认匹配和用户参数 resolvedType, false, userId);// 结束动态查询条件}collectReceiverComponents 的关键调用跨到 PMS 的 queryIntentReceivers(ActivityManagerService.java 第 16344~16358 行)。这里是节选:完整方法还要包一层 try、按 FLAG_SINGLE_USER 去重多个用户的结果,最后 return receivers。
// 定义静态组件收集方法private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,// 接收调用 uid 和用户集合 int callingUid, int[] users) {// 初始化查询结果 List<ResolveInfo> receivers = null;// 逐个用户执行查询 for (int user : users) {// 经 PackageManager Binder 接口查询静态接收者 List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()// 传入 Intent、类型、标志和用户 .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);// 结束用户遍历 }// 结束收集方法片段}
3.3 两个队列及合并顺序
无序广播存在动态接收者时,AMS 先为这批 BroadcastFilter 建立 BroadcastRecord,放入并行队列,并把 registeredReceivers 清空(ActivityManagerService.java 第 16782~16800 行)。这样,动态接收者不必等待静态组件进程创建。
// 计算动态接收者数量int NR = registeredReceivers != null ? registeredReceivers.size() : 0;// 无序且存在动态接收者时单独走并行队列if (!ordered && NR > 0) {// 选择广播队列 final BroadcastQueue queue = broadcastQueueForIntent(intent);// 为动态接收者创建广播记录 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,// 传入调用身份与匹配类型 callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,// 传入接收者列表与结果参数 appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,// 传入其余广播属性 resultExtras, ordered, sticky, false, userId);// 置位替换标志时先尝试替换同 Intent 的旧记录 final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);// 没有发生替换才追加新记录 if (!replaced) {// 加入并行队列 queue.enqueueParallelBroadcastLocked(r);// 安排队列处理 queue.scheduleBroadcastsLocked();// 结束未替换分支 }// 清空动态列表,后续只做合并 registeredReceivers = null;// 重置动态接收者数量 NR = 0;// 结束并行入队分支}仍需串行处理的动态过滤器与静态 ResolveInfo 按 priority 合并(ActivityManagerService.java 第 16841~16872 行)。
// 静态结果数量int NT = receivers != null ? receivers.size() : 0;// 最终列表下标int it = 0;// 当前静态候选ResolveInfo curt = null;// 当前动态候选BroadcastFilter curr = null;// 同时遍历静态结果与动态过滤器while (it < NT && ir < NR) {// 需要时取出下一个静态候选 if (curt == null) {// 读取静态 ResolveInfo curt = (ResolveInfo)receivers.get(it);// 结束静态候选读取 }// 需要时取出下一个动态过滤器 if (curr == null) {// 读取动态 BroadcastFilter curr = registeredReceivers.get(ir);// 结束动态候选读取 }// 动态过滤器优先级不低于静态结果时插到前面 if (curr.getPriority() >= curt.priority) {// 插入最终列表 receivers.add(it, curr);// 推进动态下标 ir++;// 清空当前动态候选 curr = null;// 推进最终列表位置 it++;// 更新最终列表长度 NT++;// 否则跳到下一个静态结果 } else {// 推进静态下标 it++;// 清空当前静态候选 curt = null;// 结束优先级判断 }// 结束合并循环}// 静态列表先遍历完时追加剩余动态过滤器while (ir < NR) {// 最终列表为空时先创建 if (receivers == null) {// 创建接收者列表 receivers = new ArrayList();// 结束列表创建 }// 追加动态过滤器 receivers.add(registeredReceivers.get(ir));// 推进动态下标 ir++;// 结束追加循环}合并后的列表进入有序队列;即使没有接收者,只要存在 resultTo,也会建立记录以返回最终结果(ActivityManagerService.java 第 16874~16892 行)。
// 接收者列表非空或存在结果接收端时才入队if ((receivers != null && receivers.size() > 0)// resultTo 存在时也要建记录以回传结果 || resultTo != null) {// 选择广播队列 BroadcastQueue queue = broadcastQueueForIntent(intent);// 创建有序广播记录 BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,// 传入调用与权限信息 callerPackage, callingPid, callingUid, resolvedType,// 传入接收者与结果参数 requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,// 传入其余广播属性 resultData, resultExtras, ordered, sticky, false, userId);// 置位替换标志时先尝试替换同 Intent 的旧记录 boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);// 没有发生替换才追加新记录 if (!replaced) {// 加入有序队列 queue.enqueueOrderedBroadcastLocked(r);// 安排队列处理 queue.scheduleBroadcastsLocked();// 结束未替换分支 }// 结束入队条件}两个入队方法直接对应两个列表,并在记录上保存入队时钟(BroadcastQueue.java 第 213~221 行)。
// 定义并行广播入队方法public void enqueueParallelBroadcastLocked(BroadcastRecord r) {// 加入并行广播列表 mParallelBroadcasts.add(r);// 记录墙上时钟时间 r.enqueueClockTime = System.currentTimeMillis();// 结束并行入队方法}// 定义有序广播入队方法public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {// 加入有序广播列表 mOrderedBroadcasts.add(r);// 记录墙上时钟时间 r.enqueueClockTime = System.currentTimeMillis();// 结束有序入队方法}3.4 FLAG_RECEIVER_REGISTERED_ONLY 改变收集范围
这个标志控制静态组件查询,不会把动态接收者改投到另一类队列。队列选择仍由 ordered、接收者类型和后续合并结果决定。动态与静态描述注册来源,并行与有序描述调度方式,两组概念分属不同维度。
四、BroadcastQueue 的投递循环
4.1 从调度消息进入处理循环
scheduleBroadcastsLocked 用 mBroadcastsScheduled 合并重复调度请求,并向队列 Handler 发送 BROADCAST_INTENT_MSG(BroadcastQueue.java 第 345~355 行)。
// 定义广播调度方法public void scheduleBroadcastsLocked() {// 已安排消息时直接返回 if (mBroadcastsScheduled) {// 结束本次调度请求 return;// 结束去重判断 }// 向 Handler 发送处理消息 mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));// 标记调度消息已发送 mBroadcastsScheduled = true;// 结束调度方法}Handler 收到消息后调用 processNextBroadcast。该方法在 AMS 锁内推进队列;一次调度可以投完当前并行记录,再处理有序队列头部的状态。

4.2 并行队列先投完
processNextBroadcast 从 mParallelBroadcasts 的头部不断取记录,并遍历记录内的全部动态接收者(BroadcastQueue.java 第 653~671 行)。这里的“并行”表示系统不等待前一个接收者完成便继续发起后续投递,并不保证各接收者回调同时占用 CPU。
// 持续处理并行广播记录while (mParallelBroadcasts.size() > 0) {// 取出队首记录 r = mParallelBroadcasts.remove(0);// 记录调度时间 r.dispatchTime = SystemClock.uptimeMillis();// 取得当前记录的接收者数量 final int N = r.receivers.size();// 遍历全部接收者 for (int i=0; i<N; i++) {// 取得当前动态过滤器 Object target = r.receivers.get(i);// 投递给动态接收者且不等待有序完成 deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);// 结束接收者遍历 }// 把已处理记录加入历史 addBroadcastToHistoryLocked(r);// 结束并行队列循环}若 mPendingBroadcast 非空,队列正在等待某个新进程。进程仍存活时,本次处理直接返回;进程已死亡时,队列恢复接收下标并清除挂起状态(BroadcastQueue.java 第 678~698 行)。
// 检查是否有等待新进程的挂起广播if (mPendingBroadcast != null) {// 声明进程死亡标志 boolean isDead;// 在 pid 表上加锁查询 synchronized (mService.mPidsSelfLocked) {// 查找挂起广播对应的进程记录 ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);// 记录缺失或进程正在崩溃都算死亡 isDead = proc == null || proc.crashing;// 结束 pid 表同步块 }// 进程还活着就继续等它 attach if (!isDead) {// 暂停本轮队列推进 return;// 进程已死亡时清理挂起状态 } else {// 把记录复位为空闲,允许重新进入分发 mPendingBroadcast.state = BroadcastRecord.IDLE;// 恢复待投递的接收者下标 mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;// 清除挂起记录 mPendingBroadcast = null;// 结束存活判断 }// 结束挂起广播判断}4.3 动态接收者的两条投递分支
deliverToRegisteredReceiverLocked 已完成权限、进程状态等检查后,把 ReceiverList 中的进程和 IIntentReceiver 交给 performReceiveLocked(BroadcastQueue.java 第 570~599 行)。
// 未被权限或状态检查拦下时执行投递if (!skip) {// 有序广播需要保存当前接收状态 if (ordered) {// 记录当前 Binder 接收端 r.receiver = filter.receiverList.receiver.asBinder();// 记录当前过滤器 r.curFilter = filter;// 让 ReceiverList 指向当前广播 filter.receiverList.curBroadcast = r;// 标记进入接收调用 r.state = BroadcastRecord.CALL_IN_RECEIVE;// 接收者所在进程存在时提升其调度优先级 if (filter.receiverList.app != null) {// 记录当前处理该广播的进程 r.curApp = filter.receiverList.app;// 进程记录当前接收者 filter.receiverList.app.curReceiver = r;// 更新进程 oom_adj mService.updateOomAdjLocked(r.curApp);// 结束进程判断 }// 结束有序状态设置 }// 投递可能抛远端异常 try {// 把进程、回调对象与结果字段交给统一投递方法 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,// 复制 Intent 并传入结果字段 new Intent(r.intent), r.resultCode, r.resultData,// 传入 extras 与广播属性 r.resultExtras, r.ordered, r.initialSticky, r.userId);// 有序广播投递后进入等待回调状态 if (ordered) {// 标记已发出接收调用 r.state = BroadcastRecord.CALL_DONE_RECEIVE;// 结束有序判断 }// 远端异常时回滚有序状态 } catch (RemoteException e) {// 记录投递失败 Slog.w(TAG, "Failure sending broadcast " + r.intent, e);// 有序广播需要清空正在处理的接收者 if (ordered) {// 清空 Binder 接收端 r.receiver = null;// 清空当前过滤器 r.curFilter = null;// 清空列表上的广播引用 filter.receiverList.curBroadcast = null;// 进程记录存在时一并清空 if (filter.receiverList.app != null) {// 清空进程的当前接收者 filter.receiverList.app.curReceiver = null;// 结束进程判断 }// 结束有序判断 }// 结束异常处理 }// 结束投递条件}统一投递方法按 ProcessRecord 是否存在分流。有应用线程时,经 scheduleRegisteredReceiver 保持与该线程其他 one-way Binder 调用的顺序;没有 ProcessRecord 时,直接调用 IIntentReceiver.performReceive(BroadcastQueue.java 第 444~462 行)。
// 定义动态接收者的统一投递方法private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,// 接收 Intent 与结果字段 Intent intent, int resultCode, String data, Bundle extras,// 接收广播属性 boolean ordered, boolean sticky, int sendingUser) throws RemoteException {// 存在进程记录时经应用线程调度 if (app != null) {// 应用线程已连接时发起调用 if (app.thread != null) {// 与其它 one-way 调用保持顺序 app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,// 传入数据、广播属性与进程状态 data, extras, ordered, sticky, sendingUser, app.repProcState);// 应用线程已死亡时无法投递 } else {// 抛远端异常,由调用方回滚有序状态 throw new RemoteException("app.thread must not be null");// 结束应用线程判断 }// 没有进程记录时直接调用 Binder 接口 } else {// 发起 IIntentReceiver 回调 receiver.performReceive(intent, resultCode, data, extras, ordered,// 传入 sticky 与用户参数 sticky, sendingUser);// 结束分支 }// 结束投递方法}4.4 Args 把回调送到主线程
回调到 ReceiverDispatcher.performReceive 后,代码创建 Args 并通过 mActivityThread.post 投递。此处的 mActivityThread 是注册时传入的 Handler;通常来自 ContextImpl 的主线程 Handler(LoadedApk.java 第 956~973 行)。
// 定义应用进程内的接收入口public void performReceive(Intent intent, int resultCode, String data,// 接收结果与广播属性 Bundle extras, boolean ordered, boolean sticky, int sendingUser) {// 封装待执行的 Runnable Args args = new Args(intent, resultCode, data, extras, ordered,// 传入 sticky 和用户参数 sticky, sendingUser);// 向注册时的 Handler 投递任务 if (!mActivityThread.post(args)) {// 投递失败且属于有序注册接收者时收尾 if (mRegistered && ordered) {// 取得 AMS 接口 IActivityManager mgr = ActivityManagerNative.getDefault();// 报告本次接收已结束 args.sendFinished(mgr);// 结束有序判断 }// 结束 post 结果判断 }// 结束接收入口}
Args.run 运行在该 Handler 所在线程。它为 extras 设置接收器类的 ClassLoader,把自身设为 PendingResult,调用 onReceive,同步回调返回后再按状态执行 finish(LoadedApk.java 第 837~901 行)。
// Args 同时是 PendingResult 和待执行的 Runnablefinal class Args extends BroadcastReceiver.PendingResult implements Runnable {// Handler 上的执行入口 public void run() {// 取得目标接收者 final BroadcastReceiver receiver = mReceiver;// 记录本次是否为有序广播 final boolean ordered = mOrdered;// 取得 AMS 接口,收尾时要用 final IActivityManager mgr = ActivityManagerNative.getDefault();// 取得本次 Intent final Intent intent = mCurIntent;// 清零成员字段,避免重复处理 mCurIntent = null;// 接收者已被反注册或已被遗忘时直接收尾 if (receiver == null || mForgotten) {// 有序广播必须通知 AMS 结束这次接收 if (mRegistered && ordered) {// 通过 sendFinished 跨进程结束 sendFinished(mgr);// 结束有序判断 }// 不再调用业务回调 return;// 结束空接收者分支 }// 业务回调可能抛异常 try {// 取接收者类的类加载器 ClassLoader cl = mReceiver.getClass().getClassLoader();// 为 Intent extras 设置类加载器 intent.setExtrasClassLoader(cl);// 为结果 extras 设置类加载器 setExtrasClassLoader(cl);// 把 Args 设为当前 PendingResult receiver.setPendingResult(this);// 调用业务接收方法 receiver.onReceive(mContext, intent);// 异常时按注册状态决定是否结束广播 } catch (Exception e) {// 有序注册接收者需要结束这次接收 if (mRegistered && ordered) {// 通过 sendFinished 跨进程结束 sendFinished(mgr);// 结束有序判断 }// 插桩未处理异常时向上抛出 if (mInstrumentation == null ||// 交给插桩对象处理 !mInstrumentation.onException(mReceiver, e)) {// 抛出运行时异常 throw new RuntimeException(// 报告出错的广播 "Error receiving broadcast " + intent// 附带接收者信息 + " in " + mReceiver, e);// 结束插桩判断 }// 结束异常处理 }// 业务代码未同步结束时补一次 finish if (receiver.getPendingResult() != null) {// 通知系统本次接收完成 finish();// 结束 PendingResult 判断 }// 结束运行入口 }// 结束 Args 类}BroadcastReceiver 本身不继承 Context。动态接收时传入的 mContext 来自注册上下文;静态接收时则使用应用的受限接收上下文。接收器通过 onReceive 参数使用上下文能力。
4.5 finishReceiver 推进有序队列
PendingResult.finish() 本身不直接跨进程:它按接收者类型判断是否需要回调,静态接收者(TYPE_COMPONENT)无条件走 sendFinished,注册接收者只在有序广播时走;sendFinished(IActivityManager am) 里的 am.finishReceiver(mToken, ...) 才是回到 AMS 的那次 Binder 调用(BroadcastReceiver.java 第 367~430 行)。
finishReceiver 负责找到当前有序记录、更新结果状态;finishReceiverLocked 表示可以继续时,再调用 processNextBroadcast(false)(ActivityManagerService.java 第 17017~17048 行)。
// 定义接收完成入口public void finishReceiver(IBinder who, int resultCode, String resultData,// 接收结果 extras、终止标志与 Intent 标志 Bundle resultExtras, boolean resultAbort, int flags) {// 清除调用身份并保存原值 final long origId = Binder.clearCallingIdentity();// 无论成败都要恢复调用身份 try {// 本轮是否要推进队列 boolean doNext = false;// 声明命中的有序记录 BroadcastRecord r;// 查找与推进必须在 AMS 锁内完成 synchronized(this) {// 按前台标志选择队列 BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0// 前台与后台队列分开维护 ? mFgBroadcastQueue : mBgBroadcastQueue;// 查找与当前 Binder 匹配的有序记录 r = queue.getMatchingOrderedReceiver(who);// 命中记录时结算本次接收 if (r != null) {// 更新结果并判断能否继续 doNext = r.queue.finishReceiverLocked(r, resultCode,// 传入结果字段与终止状态 resultData, resultExtras, resultAbort, true);// 结束记录判断 }// 结束锁内处理 }// 队列允许继续时推进下一项 if (doNext) {// 重新进入广播处理循环 r.queue.processNextBroadcast(false);// 结束继续判断 }// 顺便回收空闲进程 trimApplications();// 恢复调用身份 } finally {// 恢复 Binder 调用身份 Binder.restoreCallingIdentity(origId);// 结束身份恢复 }// 结束完成入口}有序广播的串行性由队列状态、当前接收者和完成回调共同维持。它描述接收顺序,并不表示系统把多个接收者放在同一业务线程中依次执行。
推进涉及两级下标,容易混淆。跨记录一级靠 mOrderedBroadcasts.get(0)(BroadcastQueue.java 第 715 行)与出队:只有当前记录的所有接收者都处理完(r.receivers == null || r.nextReceiver >= numReceivers,第 754~755 行)才会 remove(0) 换下一条。记录内部一级靠 int recIdx = r.nextReceiver++;(第 793 行):每投递一个接收者就自增一次,返回时把 recIdx 存进 mPendingBroadcastRecvIndex 供恢复。
五、静态注册:从 PMS 解析到进程创建
5.1 安装期解析 manifest
PackageParser 遇到 <receiver> 时复用 parseActivity,并以 isReceiver=true 解析;成功后把组件加入 owner.receivers(PackageParser.java 第 2685~2692 行)。
// 识别 receiver 标签} else if (tagName.equals("receiver")) {// 按接收者模式解析组件 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);// 解析失败时记录错误 if (a == null) {// 设置 manifest 格式错误 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;// 返回解析失败 return false;// 结束失败分支 }// 把接收者加入包对象 owner.receivers.add(a);组件内部的 <intent-filter> 被解析为 ActivityIntentInfo 并加入 a.intents;没有任何 action 的过滤器只打告警、不入列表(PackageParser.java 第 3235~3247 行)。
// 识别 intent-filter 标签if (parser.getName().equals("intent-filter")) {// 创建属于该组件的过滤信息 ActivityIntentInfo intent = new ActivityIntentInfo(a);// 解析过滤器内容 if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {// 解析失败时返回空 return null;// 结束解析失败判断 }// 没有 action 的过滤器无法匹配任何广播 if (intent.countActions() == 0) {// 只记录告警 Slog.w(TAG, "No actions in intent filter at "// 附带来源路径 + mArchiveSourcePath + " "// 附带位置描述 + parser.getPositionDescription());// 有 action 时保存到组件 } else {// 加入组件的 intent 列表 a.intents.add(intent);// 结束 action 判断 }// 结束过滤器分支}<action> 的 android:name 经过非空检查后传给 outInfo.addAction(PackageParser.java 第 4121~4131 行)。
// 识别 action 标签if (nodeName.equals("action")) {// 读取 android:name 属性 String value = attrs.getAttributeValue(// 指定 Android 资源命名空间 ANDROID_RESOURCES, "name");// name 缺失或为空时直接报错 if (value == null || value == "") {// 记录错误信息 outError[0] = "No value supplied for <android:name>";// 返回解析失败 return false;// 结束非空校验 }// 跳过当前标签的剩余内容 XmlUtils.skipCurrentTag(parser);// 把 action 写进过滤器 outInfo.addAction(value);// 结束 action 分支}5.2 发送期由 PMS 查询静态组件
PMS 收到 queryIntentReceivers 后先看 Intent 是否直接指定了组件:显式指定时只查这一个 ActivityInfo;隐式 Intent 未限定包名时调用 mReceivers.queryIntent,限定了包名时调用 queryIntentForPackage(PackageManagerService.java 第 5138~5171 行)。
// 定义静态接收者查询入口public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,// 接收目标用户 int userId) {// 用户不存在时直接返回空列表 if (!sUserManager.exists(userId)) return Collections.emptyList();// 取得 Intent 指定的组件 ComponentName comp = intent.getComponent();// 没有直接指定组件时看 selector if (comp == null) {// selector 不为空时改为按 selector 解析 if (intent.getSelector() != null) {// 用 selector 替换原 Intent intent = intent.getSelector();// 重新取组件 comp = intent.getComponent();// 结束 selector 判断 }// 结束组件判断 }// 指定了组件时只查这一个接收者 if (comp != null) {// 结果列表最多一个元素 List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);// 按组件取接收者信息 ActivityInfo ai = getReceiverInfo(comp, flags, userId);// 组件存在时包装成 ResolveInfo if (ai != null) {// 创建结果项 ResolveInfo ri = new ResolveInfo();// 保存接收者信息 ri.activityInfo = ai;// 加入结果列表 list.add(ri);// 结束组件存在判断 }// 返回显式查询结果 return list;// 结束显式组件分支 }// 隐式查询需要在包数据锁内进行 synchronized (mPackages) {// 取得 Intent 限定的包名 String pkgName = intent.getPackage();// 未限定包名时在全部接收者中匹配 if (pkgName == null) {// 使用 PMS 的接收者解析器 return mReceivers.queryIntent(intent, resolvedType, flags, userId);// 结束包名判断 }// 取出指定包 final PackageParser.Package pkg = mPackages.get(pkgName);// 包存在时只在该包的接收者中匹配 if (pkg != null) {// 限定包内的接收者集合 return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,// 传入目标用户 userId);// 结束包存在判断 }// 找不到包时返回空 return null;// 结束包数据锁 }// 结束查询入口}静态结果以 ResolveInfo 返回 AMS。此时还没有接收器 Java 实例;记录描述的是组件和应用信息,实例会在目标进程处理 H.RECEIVER 时创建。
5.3 进程状态决定直接投递或挂起
有序队列处理静态组件时,先按进程名和 uid 查 ProcessRecord。进程已连接应用线程便调用 processCurBroadcastLocked;否则调用 startProcessLocked,并把当前记录保存到 mPendingBroadcast(BroadcastQueue.java 第 1031~1089 行)。
// 按进程名与 uid 查询目标进程ProcessRecord app = mService.getProcessRecordLocked(targetProcess,// 使用接收者所属应用的 uid info.activityInfo.applicationInfo.uid, false);// 进程与应用线程都在时直接投递if (app != null && app.thread != null) {// 投递可能抛远端异常 try {// 补记包信息,供进程统计使用 app.addPackage(info.activityInfo.packageName,// 传入版本号与统计对象 info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);// 投递给已连接的进程 processCurBroadcastLocked(r, app);// 本轮处理结束 return;// 远端异常时继续走后面的启动流程 } catch (RemoteException e) {// 记录投递异常 Slog.w(TAG, "Exception when sending broadcast to "// 附带目标组件 + r.curComponent, e);// 运行时异常时丢弃这条广播 } catch (RuntimeException e) {// 记录严重错误 Slog.wtf(TAG, "Failed sending broadcast to "// 附带组件与 Intent + r.curComponent + " with " + r.intent, e);// 结束这条广播并推进队列 logBroadcastReceiverDiscardLocked(r);// 结算当前接收 finishReceiverLocked(r, r.resultCode, r.resultData,// 传入结果字段与终止状态 r.resultExtras, r.resultAbort, false);// 重新安排队列 scheduleBroadcastsLocked();// 复位记录状态 r.state = BroadcastRecord.IDLE;// 结束本轮处理 return;// 结束异常处理 }// 结束运行进程分支}// 进程不在时请求创建进程if ((r.curApp=mService.startProcessLocked(targetProcess,// 传入应用信息与启动标志 info.activityInfo.applicationInfo, true,// 合并后台来源标志 r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,// 标明启动原因是广播及目标组件 "broadcast", r.curComponent,// 传入升级与隔离参数 (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))// 判断创建结果 == null) {// 创建失败时记录日志 Slog.w(TAG, "Unable to launch app "// 附带包名 + info.activityInfo.applicationInfo.packageName + "/"// 附带 uid 与 Intent + info.activityInfo.applicationInfo.uid + " for broadcast "// 说明进程不可用 + r.intent + ": process is bad");// 丢弃这条广播 logBroadcastReceiverDiscardLocked(r);// 结算当前接收 finishReceiverLocked(r, r.resultCode, r.resultData,// 传入结果字段与终止状态 r.resultExtras, r.resultAbort, false);// 重新安排队列 scheduleBroadcastsLocked();// 复位记录状态 r.state = BroadcastRecord.IDLE;// 结束本轮处理 return;// 结束创建失败判断}// 挂起当前广播,等进程 attach 后投递mPendingBroadcast = r;// 保存当前接收者下标,供恢复使用mPendingBroadcastRecvIndex = recIdx;
5.4 进程 attach
新进程 attach 到 AMS 时,attachApplicationLocked 检查该 pid 是否对应待处理广播;条件满足便调用 sendPendingBroadcastsLocked(ActivityManagerService.java 第 6145~6148 行)。
// 新进程是否承接待投递的广播if (!badApp && isPendingBroadcastProcessLocked(pid)) {// 续投可能失败 try {// 交给各广播队列处理挂起记录 didSomething |= sendPendingBroadcastsLocked(app);// 续投抛异常说明进程有问题 } catch (Exception e) {// 记录异常并标记进程不可用 Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);// 把进程标记为 bad,后续不再复用 badApp = true;// 结束异常处理 }// 结束挂起广播判断}队列确认 pid 匹配后,先清空 mPendingBroadcast,再调用 processCurBroadcastLocked(BroadcastQueue.java 第 290~311 行)。清空动作发生在调用前,异常路径可以按常规队列状态收尾。
// 定义挂起广播续投方法public boolean sendPendingBroadcastsLocked(ProcessRecord app) {// 记录本轮是否真的投递过 boolean didSomething = false;// 取出挂起记录 final BroadcastRecord br = mPendingBroadcast;// 挂起记录的进程与新进程一致时处理 if (br != null && br.curApp.pid == app.pid) {// 续投失败要按异常路径收尾 try {// 先清除挂起标记,避免重复投递 mPendingBroadcast = null;// 向已连接的应用线程投递 processCurBroadcastLocked(br, app);// 标记本轮完成续投 didSomething = true;// 投递失败时结算这条记录 } catch (Exception e) {// 记录异常 Slog.w(TAG, "Exception in new application when starting receiver "// 附带目标组件 + br.curComponent.flattenToShortString(), e);// 丢弃这条广播 logBroadcastReceiverDiscardLocked(br);// 结算当前接收 finishReceiverLocked(br, br.resultCode, br.resultData,// 传入结果字段与终止状态 br.resultExtras, br.resultAbort, false);// 重新安排队列 scheduleBroadcastsLocked();// 复位记录状态 br.state = BroadcastRecord.IDLE;// 抛出异常交给上层处理 throw new RuntimeException(e.getMessage());// 结束异常处理 }// 结束 pid 匹配分支 }// 返回处理状态 return didSomething;// 结束续投方法}processCurBroadcastLocked 设置当前进程和接收器状态,把组件填入 Intent,随后通过 ApplicationThread.scheduleReceiver 发给应用进程(BroadcastQueue.java 第 249~288 行)。
// 定义当前静态广播的投递方法private final void processCurBroadcastLocked(BroadcastRecord r,// 接收目标进程 ProcessRecord app) throws RemoteException {// 应用线程不存在时无法投递 if (app.thread == null) {// 抛出远端异常 throw new RemoteException();// 结束空线程判断 }// 保存应用线程 Binder r.receiver = app.thread.asBinder();// 保存当前进程 r.curApp = app;// 进程记录当前正在处理的广播 app.curReceiver = r;// 提升进程状态,避免被当作后台进程回收 app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);// 更新 LRU 列表 mService.updateLruProcessLocked(app, false, null);// 更新 oom_adj mService.updateOomAdjLocked();// 把目标组件写回 Intent r.intent.setComponent(r.curComponent);// 记录是否成功发出调用 boolean started = false;// 发出调用后按结果决定是否回滚 try {// 调度静态接收者 app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,// 传入兼容性信息 mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),// 传入结果与广播属性 r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,// 传入进程状态 app.repProcState);// 调用成功 started = true;// 失败时回滚本方法设置的状态 } finally {// 没有成功发出调用 if (!started) {// 清空 Binder 接收端 r.receiver = null;// 清空当前进程 r.curApp = null;// 清空进程的当前接收者 app.curReceiver = null;// 结束回滚分支 }// 结束 finally }// 结束投递方法}5.5 应用进程反射创建接收者
应用侧 ApplicationThread.scheduleReceiver 把参数封装为 ReceiverData,更新进程状态后发送 H.RECEIVER 消息(ActivityThread.java 第 688~697 行)。
// 定义静态接收者调度入口public final void scheduleReceiver(Intent intent, ActivityInfo info,// 接收兼容性与结果参数 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,// 接收同步、用户和进程状态 boolean sync, int sendingUser, int processState) {// 更新应用进程状态 updateProcessState(processState, false);// 封装接收数据 ReceiverData r = new ReceiverData(intent, resultCode, data, extras,// 传入同步、Binder 和用户信息 sync, false, mAppThread.asBinder(), sendingUser);// 保存接收者组件信息 r.info = info;// 保存兼容性信息 r.compatInfo = compatInfo;// 向主线程发送 RECEIVER 消息 sendMessage(H.RECEIVER, r);// 结束调度入口}ActivityThread.handleReceiver 取得组件类名和包 ClassLoader,反射创建 BroadcastReceiver;随后 makeApplication 确保 Application 可用,再调用接收器(ActivityThread.java 第 2683~2743 行)。
// 定义主线程的接收处理方法private void handleReceiver(ReceiverData data) {// 取出接收者类名 String component = data.intent.getComponent().getClassName();// 取得包加载信息 LoadedApk packageInfo = getPackageInfoNoCheck(// 传入应用信息与兼容性信息 data.info.applicationInfo, data.compatInfo);// 取得 AMS 接口,用于失败时收尾 IActivityManager mgr = ActivityManagerNative.getDefault();// 声明接收者变量 BroadcastReceiver receiver;// 反射创建接收者可能失败 try {// 取得包类加载器 java.lang.ClassLoader cl = packageInfo.getClassLoader();// 为 Intent extras 设置类加载器 data.intent.setExtrasClassLoader(cl);// 准备 Intent 以进入进程 data.intent.prepareToEnterProcess();// 为结果 extras 设置类加载器 data.setExtrasClassLoader(cl);// 反射创建接收者实例 receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();// 创建失败时通知 AMS 并抛错 } catch (Exception e) {// 记录失败 if (DEBUG_BROADCAST) Slog.i(TAG,// 附带目标组件 "Finishing failed broadcast to " + data.intent.getComponent());// 通知 AMS 结束这次接收 data.sendFinished(mgr);// 抛出运行时异常 throw new RuntimeException(// 报告无法实例化接收者 "Unable to instantiate receiver " + component// 附带原因 + ": " + e.toString(), e);// 结束实例化异常处理 }// 业务回调可能抛异常 try {// 复用已存在的 Application,必要时创建 Application app = packageInfo.makeApplication(false, mInstrumentation);// 取得 Application 的基础 Context ContextImpl context = (ContextImpl)app.getBaseContext();// 把 ReceiverData 设为当前 PendingResult receiver.setPendingResult(data);// 用受限接收上下文调用业务回调 receiver.onReceive(context.getReceiverRestrictedContext(),// 传入当前广播 Intent data.intent);// 业务回调异常时同样要通知 AMS } catch (Exception e) {// 记录失败 if (DEBUG_BROADCAST) Slog.i(TAG,// 附带目标组件 "Finishing failed broadcast to " + data.intent.getComponent());// 通知 AMS 结束这次接收 data.sendFinished(mgr);// 插桩未处理异常时向上抛出 if (!mInstrumentation.onException(receiver, e)) {// 抛出运行时异常 throw new RuntimeException(// 报告无法启动接收者 "Unable to start receiver " + component// 附带原因 + ": " + e.toString(), e);// 结束插桩判断 }// 结束业务异常处理 }// 业务代码未同步结束时补一次 finish if (receiver.getPendingResult() != null) {// 通知 AMS 本次处理结束 data.finish();// 结束 PendingResult 判断 }// 结束主线程处理方法}这条链路解释了 Application 与接收器实例的先后关系:makeApplication 在 onReceive 前执行,而接收器实例由每次静态投递路径反射创建。
六、某些真机为何收不到静态广播
6.1 ROM 改动影响的是进程创建环节
AOSP android-6.0.0_r1 在目标进程不可用时调用 startProcessLocked,并用 mPendingBroadcast 保存当前记录。某些厂商 ROM 若改写或删除这处进程创建逻辑,静态接收器对应的进程就不会再按这条 AOSP 路径启动,后续 attach 与续投也就无从发生。这个判断只适用于能够确认存在此类改动的 ROM,不能扩展为所有厂商版本的共同表现。

七、小结
7.1 动态与静态是两条收集路径
动态接收者来自 AMS 的 mReceiverResolver,静态接收者来自 PMS 的组件解析结果。并行队列和有序队列处理的是调度语义。动态接收者可以进入并行记录,也可以与静态结果按 priority 合并后进入有序记录。
7.2 BroadcastReceiver 不提供 Context 能力
动态接收时,ReceiverDispatcher.Args 调用 receiver.onReceive(mContext, intent);静态接收时,ActivityThread 调用 receiver.onReceive(context.getReceiverRestrictedContext(), data.intent)。两条路径都由系统提供 Context 参数,接收器对象本身不承担 Context 的职责。
7.3 有序广播的串行由队列推进
AMS 保存当前接收者,等待 finishReceiver 后推进下一项。应用回调通常落到主线程 Handler,但“有序”指跨接收者的队列次序,不等价于所有相关进程共享一条执行线程。
7.4 mPendingBroadcast 对应待启动进程的当前记录
mPendingBroadcast 保存的是有序队列当前因进程启动而暂停的 BroadcastRecord,并配合 mPendingBroadcastRecvIndex 恢复位置。进程 attach 后,队列清除挂起字段并续投。动态注册接收者已经绑定可回调的 IIntentReceiver,不使用这条静态组件创建路径。
本文由 AgentWorkFlow 生成