Android 发送接收广播原理

前言

本文代码基于 Android R。

注册广播接收者

Android 可以动态和静态注册广播接收者。动态广播接收者注册后被 AMS 存储在其 IntentResolver 数据结构中。而静态广播接收者开机后被 PMS 读取解析并存储在其 IntentResolver 中。

动态注册广播接收者

应用组件向 AMS 注册 IIntentReceiver 和 IntentFilter ,当 AMS 派发其它组件请求派发的 Intent 时,通过其注册的 IntentFilter 来判断是否与派发的 Intent 匹配,如果匹配,AMS 调用 IIntentReceiver 通知组件处理收到 Intent 后的操作。

每个 BroadcastReceiver 都会有其对应的 IIntentReceiver ,它由其 Context 构建。IIntentReceiver 被发送给 AMS 后与其进程信息 ProcessRecord 被封装在 ReceiverList 中,在发现匹配到的 Intent 时用来寻找 IIntentReceiver 对应进程。而 ReceiverList 被放在由 IntentFilter 构建的 BroadcastFilter 中,最后 BroadcastFilter 被缓存在 AMS 的 IntentResolver 中。
动态注册广播接收者

动态注册接收者

AMS 在分发一个 Intent 时,用 IntentResolver 中缓存的 BroadcastFilter 的 IntentFilter 和 Intent 相匹配,取出匹配成功的 BroadcastFilter 列表。再取出每个 BroadcastFilter 中的 ReceiverList ,获取其 IIntentReceiver 和进程信息,把 Intent 通过进程的 IApplicationThread 发送给对应的 IIntentReceiver ,再发送给 IIntentReceiver 对应的 BroadcastReceiver 处理。
查找动态注册的广播接收者

代码流程为:
动态注册广播接收者代码流程

静态注册广播接收者

静态注册广播接收者,那在 AndroidManifest.xml 中声明 ,并使用匹配器 IntentFilter 配置它要匹配的数据。

开机后,PMS 会去读取每个 pkg 的信息,并把每个 pkg 里的每个组件信息存储到 ComponentResolver 中,receiver 组件的信息存储在 ComponentResolver#mReceivers 中。mReceivers 是一个 IntentResolver,其方法 newResult() 负责将 F 转换成 ResolveInfo。其中 F 为从 pkg 中读取的 receiver 和 IntentFilter 的 Pair。
注册静态接收者

静态注册广播接收者

AMS 在分发一个 Intent 时,会根据 Intent 封装的数据格式去 PMS ComponentResolver 的 mReceivers 中取出对应的 Map 来查找,取出对应 Map 中的 [ParsedActivity, IntentFilter] 对,判断 IntentFilter 是否和 Intent 匹配,如果匹配,通过 IntentFilter#newResult 方法将 [ParsedActivity, IntentFilter] 转换成 ResolveInfo 返回给 AMS。AMS 根据 ResolveInfo 中的 ActivityInfo 找到接收者进程,把 Intent 发送给接收者进程后,再实例化一个和 ActivityInfo 对应的 BroadcastReceiver 实例。


获取静态广播接收者

PMS 查找和 Intent 对应的 ResolveInfo

分发广播

根据 AMS 分发广播的方式,可以分为串行分发和并行分发。

当 Intent 设置为有序分发(ordered)时,它会被串行分发。
当接收者为静态注册时,发送给它们的 Intent 会被串行分发。
仅 Intent 没有设置为有序分发,且接收者为动态注册,才会被并行分发。

并行分发不关注分发结果,一次性全部分发给所有接收者,并在串行分发前分发。
串行分发需要等上一个接收者处理完成后才会分发给下一个接收者。


发送广播

并行分发广播

AMS 为 Intent 和所有并行接收者创建广播 BroadcastRecord,再根据 Intent 设置的属性判断它是由前台还是后台或是耗时 BroadcastQueue 分发。BroadcastRecord 会被插到对应 BroadcastQueue 并行广播列表的末尾,等待派发。


AMS 并行分发广播

BroadcastQueue 分发广播

BroadcastQueue 分发并行广播

串行分发广播

如果 Intent 被要求有序派发,AMS 将它的所有接收者和 Intent 封装成 BroadcastRecord ,找到要派发它的 BroadcastQueue ,然后把 BroadcastRecord 插入到对应 BroadcastQueue 的 BroadcastDispatcher 的有序广播队列中等待派发。如果是非有序派发的 Intent,AMS 将其所有静态注册的接收者和 Intent 封装成 BroadcastRecord。

BroadcastDispatcher 负责为 BroadcastQueue 串行派发广播。它加入了一个延迟策略,来特别关注耗时的接收者,以减少这些耗时者对性能效率的影响。延迟策略下一小节将单独描述。


AMS 串行分发广播

BroadcastQueue 分发串行广播

延迟策略

延迟策略主要是为了减小耗时接收者对整个广播发送性能产生的影响。当一个接收者接收并处理 Intent 耗时超过系统设置的域值(默认 5s)时,这个接收者所在的进程被认为是低效进程,所有之后发送给它的 Intent 将被延迟发送,直到系统判定它恢复了正常。

当一个进程被判定为低效进程时,BroadcastDispatcher 为它创建一个 Deferrals ,负责管理延迟发送给他的 Intent:它包含了延迟开始时间、延迟时长、延迟结束时间、延迟的广播列表(BroadcastQueue 会为某个 Intent 在这个进程中的所有接收者和 Intent 单独创建一个广播 BroadcastQueue,因此这个广播列表中包含了一队由不同的 Intent 和其在该进程中的接收者构成的广播)。

当一个进程的 Deferrals 被创建后会被插入到 BroadcastDispatcher 的延迟进程广播列表中,当其延迟到期后,会优先在下一次派发时派发。派发时会更新下一次的延迟时长(即 Deferrals 的广播列表中下一个广播被延迟派发的时长),默认为 上一次延迟时长*缩短因子(0.75f)。

当被延迟的进程正在接收 alarm 广播时,它的 Deferrals 会被移到 Alarm 延迟进程广播列表,直到它处理完 alarm 广播处理。Alarm 延迟进程广播列表中的广播具有最高优先级的派发顺序。

当 Deferrals 进程中的某个接收者在处理一个 Intent 时仍耗时超过 5s ,它下一次的延迟时长会被重新设置为 5s。

当系统中所有的串行广播都被发送完成时,会立即派发延迟进程广播队列中的广播而不管它是否到期。

接收者在接收处理某个 Intent 结束时,AMS 计算其耗时并判断这个接收者所在进程是否为低效进程,是否需要对它使用延迟策略:
广播发送给某个 receiver 完成

延迟策略概要:
延迟规则设计

总结

Intent 会根据其发送者要求的发送规则(是否有序)和接收者的注册方式被 AMS 以并行或串行方式分发。

Intent 和其接收者列表构成广播,插入到 AMS 为它分配的 BroadcastQueue 的并行或串行广播队列中,等待派发。

并行派发不关注接收者的处理结果。串行派发关注处理结果,如果处理耗时会对接收者进程采用延迟策略,且仅当上一个接收者接收完后才会发送给下一个接收者。ordered 广播发送给动态注册的接收者时,通过 BroadcastRecord.state 来判断上一个接收者是否接收完成,发送给 receiver 之前为 CALL_IN_SERVICE,发送之后为 CALL_DONE_RECEIVE,接收者处理完后为 IDLE。


发送广播

原创文章,欢迎转载,但请注明出处。

你可能感兴趣的:(Android 发送接收广播原理)