广播 Broadcast
属于 Android
四大组件之一,通过观察者模式,用于实现进程间的异步通信。
广播的实现机制是观察者模式;消息发布/订阅模式的事件驱动模型,消息的生产者发布事件,而使用者订阅感兴趣的事件。Broadcast
机制和 Binder
机制不一样的地方在于:广播的发送者和接收者事先是不需要知道对方的存在的,这也是观察者模式的特点,显而易见的优点是系统的各个组件可以松耦合地组织在一起,所有的广播发送和接收都是通过系统 AMS
来调度的,它充当了广播总线的功能。
基础
广播实现进程间异步交互:进程间通信时通过 Binder
来实现的,异步通信是通过 Handler
实现。
广播接收器的分类
- 静态注册广播接收器:
Manifest-declared receivers
在AndroidManifest.xml
文件中通过<receiver>
声明的广播接收器。 - 动态注册广播接收器:
Context-registered receivers
通过registerReceiver
注册的广播接收器。
广播的分类
- 普通广播
通过sendBroadcast
发出的广播。 - 有序广播
通过sendOrderedBroadcast
发出的广播;有序广播对应的广播接收器存在优先级关系,优先级越高越先收到广播。有序广播还可以有回调广播接收器,即广播匹配的所有广播接收器处理完毕后,系统会再向广播发送者,发回这个回调。 - 粘性广播
通过sendStickyBroadcast
发出的广播。粘性广播发送后,系统会把代表这个广播的Intent
保存下来,如果后面在动态注册相同Action
的广播接收器时,系统就将最后发出的粘性广播重新发送给这个广播接收器。也就是说,尽管广播早就发送处理完毕了,但是后续如果有新注册的广播接收器能匹配这个广播,还是会响应广播事件的。但是粘性广播从Android 5.0
开始就将相关系统接口全部标注为@Deprecated
。它们不提供安全性(任何人都可以访问它们),没有保护(任何人都可以修改它们)以及许多其他问题。所以Android
平台已经弃用,本文也不做介绍。
广播的标记
根据发送广播 Intent
设置的标记来区分:
FLAG_RECEIVER_FOREGROUND
表示广播为前台广播,允许接收者以前台的优先级运行,有更短的时间间隔。默认情况下广播都是后台广播FLAG_RECEIVER_REPLACE_PENDING
表示新的广播会替换掉那些已存在的相同广播。相同的定义是通过Intent.filterEquals
方法对两个广播的Intent
比较,当匹配到相同的,新的广播和对应的接收器会将待发送的广播列表中已存在的替换掉,在列表中保留同样的位置。这个标志通常被粘性广播使用,只保证将最新的广播的值传递给接收器。
广播的 ANR
前台广播的 ANR
时间是 10 秒,后台广播是 60 秒。因为默认为后台广播,所以通常情况下,需要在 60 秒内处理完所有的广播事件。广播的 ANR
只会出现在有序处理的广播队列中(包含有序广播和静态注册的广播接收器匹配的普通广播)中。
1 | // ActivityManagerService.java |
相关类文件
速查表
1 | framework/base |
类简介
BroadCastReceiver
广播接收器,用于处理接收的每个广播事件。IIntentReceiver
是AIDL
文件,每个BroadCastReceiver
都会转换为一个IIntentReceiver
,用来跨进程传递数据。LoadedApk.ReceiverDispatcher
广播接收发布器,BroadCastReceiver
都是在这转换为IIntentReceiver
的。每个动态注册的广播接收器BroadcastReceiver
都对应一个LoadedApk.ReceiverDispatcher
,最终都是在这进行响应调用onReceive
的。BroadcastFilter
是IntentFilter
的子类,广播过滤器,用于匹配广播Intent
;包含广播接收者相关信息,权限等。只有动态注册广播广播接收器会用到。ReceiverList
是ArrayList
的子类,是一个集合用来存储BroadcastFilter
。记录广播接收器相关信息等,只有动态注册的广播接收器会用到,每个BroadCastReceiver
对应一个ReceiverList
,而每个ReceiverList
可以包含多个BroadcastFilter
,表示能够监听多个广播。BroadcastRecord
继承了Binder
,用来跨进程传递数据。发送的每个广播Intent
,最后都会对应一个BroadcastRecord
;它记录了广播的发送时间,发送者,接受者,广播携带的数据,回调,所在BroadcastQueue
等等。BroadcastQueue
广播队列,有两个ArrayList<BroadcastRecord>
用于存储BroadcastRecord
分别是:mParallelBroadcasts
存储并行处理的广播;mOrderedBroadcasts
存储有序处理的广播。ContextImpl
广播发送,广播接收器动态注册的入口。ActivityThread
应用主线程,广播发送过程都是由它进入系统进程AMS
,广播接收器响应事件是有系统AMS
进入它。ActivityManagerService
AMS
系统四大组件大管家,类似广播总线:管理广播接收器的注册,管理广播的发送到匹配的广播接收器中处理。PackageParser
包解析类,会从AndroidManifest.xml
中解析出静态注册的广播接收器。PackageManagerService
PMS
系统包管家,负责静态注册广播接收器的存储,查询。
BroadcastFilter
类详解
BroadcastFilter
继承了 IntentFilter
类,广播信息过滤器,即动态注册广播接收器时,注册的是哪个广播过滤器。
源码分析
1 | final class BroadcastFilter extends IntentFilter { |
成员变量
receiverList
:当前对象属于哪个接收列表packageName
:动态注册广播发起者的包名requiredPermission
:设置发送广播时需要的权限owningUid
:动态注册广播发起者Uid
owningUserId
:动态注册广播发起者UserId
这里简单介绍下 uid, userId
的区别:
uid
是Linux
下的概念,表示Linux
系统下的用户ID
。userId
是Android
的多用户概念,从 4.2 开始系统支持多用户登录,记录登录用户的ID
;不支持多用户登录的系统上,这个值是唯一的。多用户支持是在frameworks/base/core/res/res/values/config.xml
文件中,config_multiuserMaximumUsers
值来决定的。
ReceiverList
类详解
ReceiverList
继承了 ArrayList
,本质上是一个数据集合,数据类型为 BroadcastFilter
。动态注册广播接收器时,每个广播接收器 BroadCastReceiver
对应一个 ReceiverList
,记录了一系列 BroadcastFilter
的列表,表示这个广播接收器注册了一个或多个广播过滤器。
源码分析
1 | final class ReceiverList extends ArrayList<BroadcastFilter> |
成员变量
ActivityManagerService owner
:AMS
对象IIntentReceiver receiver
:当前ReceiverList
对应的IIntentReceiver
ProcessRecord app
:动态注册广播发起者进程相关信息pid
:动态注册广播发起者进程Id
uid
:动态注册广播发起者uid
userId
:动态注册广播发起者userId
BroadcastRecord curBroadcast
:动态注册的广播,其详细信息linkedToDeath
:Binder
通信机制是否断开stringName
:ReceiverList
转换成的字符串
BroadCastReceiver
类详解
BroadCastReceiver
广播接收器,用来处理监听的广播事件。BroadcastReceiver
是抽象类,抽象方法为 onReceive
,注册广播接收器前,需要在其子类中实现。
PendingResult
类
PendingResult
是广播接收器待处理的结果类,是异步结果类;简要介绍下这个类存在的意义:
通常情况下,UI
主线程阻塞超过 5 秒就会产生 ANR
,更不用说卡顿现象 UI Jank
;所以广播接收器中如果有耗时工作(比如磁盘读写,网络下载等),需要及时切换到后台工作线程中执行;而 PendingResult
就存储了广播处理器接收到广播所有相关信息,将 PendingResult
对象传递到后台工作线程完成广播处理,处理完后需要调用 PendingResult.finish()
通过系统该广播接收器已经处理完毕。 PendingResult
类有两个注意事项:
PendingResult
类中的状态不是线程安全的,使用时将整个对象交给后台工作线程,避免并发竞争,该线程将负责设置结果并最终调用finish()
- 系统中关于广播接收器的
ANR
:前台广播接收器允许运行时间大概是 10 秒,后台广播是 60 秒,如果广播处理器没有处理完毕就会产生ANR
。注意这个过程的计算时间是:从广播分发到广播处理器,直到广播处理器通知系统处理完毕的总时间。即使通过PendingResult
切换到后台工作线程,直到调用PendingResult.finish()
,这整个过程都受到了这个ANR
时间的限制。所以长时间的工作是不能放到BroadCastReceiver
中来实现的,PendingResult
仅仅是多争取到了几秒钟
PendingResult
类的两个子类
PendingResult
类的构造方法是 @hide
隐藏的,通常在 BroadcastReceiver.onReceive()
中调用 goAsync()
返回得到;而实际构造是在其子类中,静态注册广播和动态注册广播对应不同的子类:
ActivityThread.ReceiverData
对应静态注册广播1
2
3
4
5
6
7
8
9
10
11
12
13
14
15static final class ReceiverData extends
BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent,int resultCode,String resultData,
Bundle resultExtras, boolean ordered, boolean sticky,
IBinder token, int sendingUser) {
super(resultCode, resultData, resultExtras, TYPE_COMPONENT,
ordered, sticky,token, sendingUser, intent.getFlags());
this.intent = intent;
}
Intent intent;
ActivityInfo info;
CompatibilityInfo compatInfo;
public String toString() {...}
}LoadedApk.ReceiverDispatcher.Args
对应动态注册广播1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19final class Args extends BroadcastReceiver.PendingResult
implements Runnable {
private Intent mCurIntent;
private final boolean mOrdered;
private boolean mDispatched;
public Args(Intent intent, int resultCode, String resultData,
Bundle resultExtras, boolean ordered,
boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
ordered, sticky, mIIntentReceiver.asBinder(),
sendingUser, intent.getFlags());
mCurIntent = intent;
mOrdered = ordered;
}
public void run() {...}
}
PendingResult
类源码分析
PendingResult
对应的广播类型有三种:
TYPE_COMPONENT
:静态注册广播TYPE_REGISTERED
:动态注册广播TYPE_UNREGISTERED
:取消广播注册
1 | public static class PendingResult { |
BroadcastReceiver
源码分析
1 | public abstract class BroadcastReceiver { |
BroadcastReceiver
重要方法
BroadcastReceiver
大部分数据存储都是通过 PendingResult
实例来保存的。
onReceive
抽象方法,子类必须实现;广播接收器处理的入口。goAsync
异步广播,返回PendingResult
实例,进入后台线程处理;处理完毕后必须调用PendingResult.finish()
结束。abortBroadcast
如果广播接收器收到的是有序广播,则终止有序广播继续向下传递。
IIntentReceiver
详解
IIntentReceiver
是一个 AIDL
文件,所以 IIntentReceiver
接口继承了 Binder
机制中的 IInterface
,表示服务端拥有的能力或者说能提供的功能。 IIntentReceiver
声明的方法,是在 LoadedApk.ReceiverDispatcher.InnerReceiver
中实现的。IIntentReceiver.aidl
源码如下:
1 | // frameworks/base/core/java/android/content/IIntentReceiver.aidl |
BroadCastReceiver
是系统组件,并不支持跨进程传输,Android Broadcast
机制使用 IIntentReceiver
来进行代替 BroadCastReceiver
来进行跨进程传输。
可以看出 IIntentReceiver
只提供了一个方法 performReceive
,该方法仅仅在动态注册的广播接收器中会触发调用(静态注册广播接收器在 ActivityThread
中通过反射直接调用 onReceive
回调);系统进程 AMS
在分发广播处理事件时,会向注册了广播接收的应用进程调用该方法,最终调用 BroadcastReceiver.onReceive
触发回调。注意:方法使用了 oneway
关键字,表示非阻塞调用;即 AMS
进程分发广播事件时,不需要等待每个应用接收者执行完 onReceive
。
LoadedApk
类
LoadedApk
类存储了当前加载的 Apk
相关信息,包括主线程,文件所在路径,类加载器,注册的广播列表,启动的服务列表等等;LoadedApk
类是在每个应用加载初始化进程、主线程时实例化的,每个应用对应一个 LoadedApk
实例。
本文只分析和广播 Broadcast
相关内容,LoadedApk
中存储了当前 Apk
中所有的普通广播接收器 BroadCastReceiver
,即通过 registerReceiver
注册到系统中的广播接收器。
而有序广播中的回调广播接收器,即广播发送出去后,等待所有广播接收器处理完毕,最后产生一个回调给广播发送者,作用类似 startActivityForResult
。回调 BroadCastReceiver
不需要通过 registerReceiver
注册到系统中,是以参数的形式在 sendOrderedBroadcast()
发送广播时传递出去,这类广播不会存储到 LoadedApk
中。
在 ContextImpl
代码中可以看到:LoadedApk
对这两类广播接收器的处理过程:
1 | // ContextImpl.java |
部分源码分析
1 | public final class LoadedApk { |
成员变量
mReceivers
:当前Apk
注册的广播列表mUnregisteredReceivers
:当前Apk
取消广播注册的列表
这两个成员变量都是 Map
结构,Key
值都是 Context
,Value
仍然是一个 Map
结构:
Key: Context
每个Activity
或者每个Service
注册的广播分开记录,方便快速查找某个Context
实例注册的所有广播。Value: ArrayMap<BroadcastReceiver, ReceiverDispatcher>
BroadcastReceiver
和 ReceiverDispatcher
是一一对应的,每个广播接收器都会对应一个广播接收分发器。ContextImpl.sendOrderedBroadcast, ContextImpl.registerReceiver
会对每个广播接收器 BroadCastReceiver
,通过 LoadedApk
找到或者生成对应的 ReceiverDispatcher
;在发送有序广播或者注册广播接收器,等到广播接收者开始执行时,AMS
通过 ReceiverDispatcher.InnerReceiver
来触发回调,即调用 BroadCastReceiver.onReceive
。
ReceiverDispatcher
静态内部类
ReceiverDispatcher
是 LoadedApk
的静态内部类,同时它自己还包含两个内部类:
- 静态内部类
InnerReceiver
实现了IIntentReceiver.performReceive
方法,实际最终调用的是外部类ReceiverDispatcher.performReceive
方法。 - 成员内部类
Args
继承了BroadCastReceiver.PendingResult
类,可以执行部分异步操作;同时实现了Runnable
,通过传入的Handler
发布这个任务,并调用BroadCastReceiver.onReceive
,处理广播接收器回调事件。处理完后,通知系统该广播接收器处理完毕。 - 成员变量
mRegistered
:记录该广播接收器是否通过registerReceiver
注册的,即是否为动态注册的广播接收器。
ReceiverDispatcher
的几个重要方法:
getIIntentReceiver
:返回广播接收器BroadCastReceiver
对应的IIntentReceiver
对象performReceive
:使用当前线程Handler
发送Args
对象并执行;所有动态注册的广播接收器,都是在这里回调onReceive
方法
广播相关方法
getReceiverDispatcher
根据广播接收器获取广播接收分发器的IIntentReceiver
,用于跨进程传递。主要是从mReceivers
中获取;如果不存在则新建。forgetReceiverDispatcher
从mReceivers
中移除指定回调广播接收器,并放入mUnregisteredReceivers
中,返回值也是该广播接收器对应的IIntentReceiver
。
BroadcastRecord
类详解
BroadcastRecord
继承了 Binder
,表明该对象作为数据容器,能被跨进程通信。BroadcastRecord
保存了广播发送者相关信息,包含发送者的:包名,进程信息,广播是否有序,请求的权限等等。
源码分析
1 | /** |
重要成员变量
Intent intent
:广播发送时的Intent
ComponentName targetComp
:广播发送者组件名称ProcessRecord callerApp
:广播发送者进程记录String callerPackage
:广播发送者包名callingPid
:广播发送者Pid
callingUid
:广播发送者Uid
userId
:广播发送者userId
ordered
:广播是否有序发送sticky
:广播是否为粘性广播initialSticky
:广播是否一开始就设置为粘性广播resolvedType
:Intent
的类型String[] requiredPermissions
:广播发送者拥有的广播权限appOp
:广播对应的应用管理权限BroadcastOptions options
:广播发送者设定的选项List receivers
:广播接收者列表,特别需要注意的是:它包含了两种类型数据ResolveInfo, BroadcastFilter
,分别对应静态注册广播接收器和动态注册广播接收器,取出时需要做类型判断int[] delivery
:每个广播接收者的投递状态,状态分为四种IIntentReceiver resultTo
:有序广播中回调的广播接收器,它也是有序广播的最后一个接收者enqueueClockTime
:广播进入队列的时间dispatchTime
:开始分派广播的开机时间dispatchClockTime
:开始分派广播的系统绝对时间receiverTime
:广播接收者接收到的时间finishTime
:广播接收者处理完广播的完成时间resultCode
:广播处理完毕的结果码resultData
:广播处理完毕的简单数据resultExtras
:广播处理完毕的复杂数据resultAbort
:广播是否被终止int nextReceiver
:下一个广播接收者索引IBinder receiver
:广播当前接收者int state
:广播的当前状态,状态分为五种anrCount
:当前广播出现的ANR
次数manifestCount
:分发给静态注册广播接收者的次数manifestSkipCount
:分发给静态注册广播接收者跳过的次数BroadcastQueue queue
:处理当前广播所在队列BroadcastFilter curFilter
:处理广播时当前广播过滤器ProcessRecord curApp
:广播当前接收者的进程信息ComponentName curComponent
:广播当前接收者的组件信息ActivityInfo curReceiver
:广播当前接收者的信息
cleanupDisabledPackageReceiversLocked
方法
BroadcastRecord
中只有一个包内可见的方法 cleanupDisabledPackageReceiversLocked
,该方法功能是从 List receivers
清理掉已经取消注册的广播注册接收者。
BroadcastQueue
类详解
BroadcastQueue
用来保存系统和应用发出的广播信息;通常使用两个广播队列 BroadcastQueue
:一个用来保存前台广播,一个用来保存后台广播;而每个广播队列 BroadcastQueue
包含两个列表:一个用来并行处理;一个用来有序处理。
重要成员变量
ArrayList<BroadcastRecord> mParallelBroadcasts
存储并行处理的广播:首先它是普通广播,其次这些广播匹配的广播接收器必须是动态注册的。ArrayList<BroadcastRecord> mOrderedBroadcasts
存储有序处理的广播:包含有序广播和普通广播(匹配的广播接收器时静态注册的)。mTimeoutPeriod
广播接收器ANR
超时时间阀值。前台广播是 10 秒,后台广播是 60 秒。
BroadcastHandler
类
BroadcastHandler
是成员内部类,用来处理下一条广播,或者处理广播超时 ANR
。
1 | static final int BROADCAST_INTENT_MSG = |
AppNotResponding
类
AppNotResponding
成员内部类,广播接收器处理广播事件时发生 ANR
。
1 | private final class AppNotResponding implements Runnable { |
静态注册广播接收器流程分析
静态注册的广播接收器,又称为 Manifest
文件声明广播接收器,是在 PMS
扫描 AndroidManifest.xml
信息时解析的;所有静态注册的广播接收器,统一由 PMS
保存,广播在发送过程中,查找匹配的静态注册广播接收器也是在 PMS
中查询。
流程图
PackageParser.parseBaseApplication
详解
PackageParser
主要用于详细解析包内相关信息,而 parseBaseApplication
主要解析 AndroidManifest.xml
,将每个 TAG
都解析并保存,其中静态注册的广播接收器存储到 receivers
中,而 PMS
会根据每个包解析的数据,保存整个系统全部的静态注册广播接收器(当然也保存其他 Activity, Services, ContentProvider
等信息)。
1 | // PackageParser.java |
PMS
静态注册广播接收器
PMS
在调用完 PackageParser.parsePackage
解析完指定包名信息后,将存储在 PackageParser.Package.receivers
的静态广播接收器信息,再保存到 PMS.mReceivers
中,方便后续全局查询。
1 | // PackageManageServices.java |
PMS
查询静态注册广播接收器
AMS
在发送广播时,会从 PMS
中查询匹配的静态广播接收器;PMS.queryIntentReceiversInternal
根据广播 Intent
,从 mReceivers
中查询匹配的 ResolveInfo
,而 ResolveInfo.activityInfo
存储了对应的广播接收器。
1 | // PackageManageServices.java |
protected-broadcast
受保护的广播
在 AndroidManifest.xml
中声明的 protected-broadcast
受保护广播 ACTION
,表示它们只能被有系统权限的广播接收器接收。在 PackageParser.Package.protectedBroadcasts
中保存,同时 PMS.mProtectedBroadcasts
会做系统全局保存, AMS
发送广播时会查询 PMS
做权限检查,如果没有权限则不会分发给该广播接收器。
动态注册/取消广播接收器流程分析
流程图
动态注册 ContextImpl/LoadedApk
动态注册时统一由 ContextImpl.registerReceiverInternal
向系统注册广播接收器。这个过程主要功能:
- 将
BroadCastReceiver
转换为对应的IIntentReceiver
,用于跨进程通信 LoadedApk.getReceiverDispatcher
会将当前Apk
注册的每个广播接收器保存到mReceivers
中;如果是有序广播的回调广播接收器则不保存- 向
AMS
中注册IIntentReceiver
1 | // LoadedApk.java |
动态注册 AMS
广播接收器动态注册时,广播接收器的相关信息保存在 AMS
中:
mRegisteredReceivers
保存所有动态注册的广播接收器对应的ReceiverList
。mRegisteredReceivers
并没有太多作用,注册保存信息,取消注册时从这里删除。mReceiverResolver
保存所有所有动态注册广播接收器时对应的广播过滤器BroadcastFilter
。广播发送过程中,会根据Intent
从mReceiverResolver
中查找BroadcastFilter
;每个BroadcastFilter
都保存了它所属的ReceiverList
;而ReceiverList
保存了BroadCastReceiver
对应的IIntentReceiver
;动态注册广播接收器事件处理时,使用IIntentReceiver
处理onReceive
回调。
主要功能:
- 获取动态注册广播接收器,应用进程相关信息
- 处理粘性广播事件
BroadCastReceiver
生成对应的ReceiverList
和BroadcastFilter
- 将
ReceiverList
添加到mRegisteredReceivers
中 - 将
BroadcastFilter
添加到mReceiverResolver
中
1 | // ActivityManagerService.java |
取消注册 ContextImpl/LoadedApk
取消注册时统一由 ContextImpl.unregisterReceiver
向系统取消注册广播接收器。这个过程主要功能:
- 将
BroadCastReceiver
转换为对应的IIntentReceiver
,用于跨进程通信 LoadedApk.forgetReceiverDispatcher
会将当前需要取消注册的广播接收器,从mReceivers
中移除;如果需要调试,会同时将它加入mUnregisteredReceivers
中- 向
AMS
中取消注册IIntentReceiver
1 |
|
取消注册 AMS
广播接收器取消注册时,从 AMS
中移除对应的数据,主要功能为:
- 从
mRegisteredReceivers
中移除ReceiverList
- 从
mReceiverResolver
中移除ReceiverList
中所有的BroadcastFilter
- 如果还存在没有处理完的广播接收器,触发
BroadcastQueue.processNextBroadcast
继续处理
1 | // ActivityManagerService.java |
广播发送流程分析
先回顾下基础概念,广播的类型:
- 普通广播:通过
sendBroadcast
发送的广播 - 有序广播:通过
sendOrderedBroadcast
发送的广播
系统对广播响应并处理的方式分为:
- 并行处理
广播存储位置:BroadcastQueue.mParallelBroadcasts
;并行处理的广播为:普通广播,且它对应的广播接收器一定是动态注册的。 - 有序处理
广播存储位置:BroadcastQueue.mOrderedBroadcasts
;有序处理的广播为:有序广播和普通广播(对应的广播接收器是静态注册的)。mOrderedBroadcasts
中处理普通广播(对应的广播接收器时静态注册的)非常费解,初看代码时很容易搞错,存储逻辑是在AMS
调度时决定的。
流程图
- 广播发送过程,从广播发送所在应用进程进入系统进程
Context -> AMS -> BroadcastQueue
- 广播发送过程,从系统进程进入广播接收器应用所在进程
BroadcastQueue -> ActivityThread -> BroadCastReceiver.onReceive
ContextImpl.sendBroadcast
发送普通广播
ContextImpl
对于普通广播并不做特别处理,比较简单,直接将 Intent
和广播发送应用相关信息传递给 AMS
,进入系统来处理分发流程。
1 |
|
ContextImpl.sendOrderedBroadcast
发送有序广播
有序广播支持广播接收器回调,所以进入系统 AMS
处理前,会先将 BroadCastReceiver
转换为 IIntentReceiver
,回调广播接收器在 LoadedApk
中不做保存。
1 | void sendOrderedBroadcast(Intent intent, String receiverPermission, |
AMS.broadcastIntentLocked
广播发送解析
不管是普通广播还是有序广播,在 AMS
中都是进入了 broadcastIntentLocked(Intent, ...)
方法来调度处理的。这个方法中会处理很多系统级广播,比如 Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_TIME_CHANGED
等等,在 AMS
做预处理或者直接处理完毕。该方法主要功能:
- 各种权限检查
- 系统广播处理
- 收集该广播匹配的所有动态注册广播接收器
- 收集该广播匹配的所有静态注册广播接收器
- 将广播对应的广播记录存入广播队列(如果是有序广播,会按照优先级先排序再存入)
代码分析(代码有简化和调整位置)如下:
1 | // ActivityManagerService.java |
BroadcastQueue.processNextBroadcast
广播发送解析
不管是并行处理还是有序处理,在 BroadcastQueue
中都是进入 processNextBroadcast(boolean fromMsg)
方法处理的。该方法主要功能:
- 并行处理的广播接收器,一次全部处理完
- 有序广播处理完毕后,处理回调广播接收器
- 各种权限检查
- 调用
deliverToRegisteredReceiverLocked
处理所有动态注册的广播接收器 - 如果是静态广播接收器,开启对应的应用进程
- 调用
processCurBroadcastLocked
处理所有静态注册的广播接收器
1 | // BroadcastQueue.java |
deliverToRegisteredReceiverLocked
方法解析
BroadcastQueue.deliverToRegisteredReceiverLocked
专门用来处理动态注册的广播接收器,最终是在 performReceiveLocked
方法中调用了 ActivityThread.scheduleRegisteredReceiver
,进入广播接收器所在进程处理具体的回调 onReceive
。这个阶段主要功能:
- 各种权限检查
- 添加完善广播记录
- 跨进程进入广播接收器所在应用进程,调用
onReceive
1 | private void deliverToRegisteredReceiverLocked(BroadcastRecord r, |
ActivityThread.scheduleRegisteredReceiver
方法解析
每个应用的主线程中 ActivityThread.scheduleRegisteredReceiver
来执行广播处理事件,performReceive
对应的是 LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive
方法。
1 | public void scheduleRegisteredReceiver(IIntentReceiver receiver, |
processCurBroadcastLocked
方法解析
BroadcastQueue.processCurBroadcastLocked
专门处理静态注册的广播接收器。该方法主要功能:
- 将
BroadCastReceiver
子类存储到intent
中 - 跨进程进入静态广播接收器所在进程,通过反射调用
onReceive
1 | private final void processCurBroadcastLocked(BroadcastRecord r, |
ActivityThread.scheduleReceiver
方法解析
ActivityThread.scheduleReceiver
处理静态注册的广播接收器,从 intent
中获取具体的广播接收器子类组件名,在 handleReceiver
中使用反射技术生成对应的实例对象,并调用其 onReceive
方法。
1 | public final void scheduleReceiver(Intent intent, ActivityInfo info, |
AMS.finishReceiver
结束广播
所有的广播接收器处理完毕后,最终会调用系统 ActivityManagerService.finishReceiver
结束整个流程。结束流程前会再次检查确认,是否还有广播接收器没有处理完(静态注册的广播接收器,如果应用没有启动,会先启动应用进程),存在的话就触发 BroadcastQueue.processNextBroadcast
继续分发。
1 | // ActivityManagerService.java |
广播 ANR
processNextBroadcast
中可以看到,广播的 ANR
只会出现在有序处理的广播队列 mOrderedBroadcasts
中,也就是普通广播匹配的动态注册的广播接收器不会出现 ANR
。
setBroadcastTimeoutLocked
:设置广播超时延时消息broadcastTimeoutLocked
:当广播接收者等待时间过长,触发ANR
机制cancelBroadcastTimeoutLocked
:当广播正常执行完毕清除
超时时间计算方式:从队列中取出有效广播开始计时,直到所有的广播接收器处理完毕,才取消计时。
示例:发送一个有序广播,AndroidManifest.xml
中静态注册的广播接收器和一个动态注册的广播接收器都能匹配到,同时有序广播带有回调广播接收器,也就是说该广播一共会有三个 BroadCastReceiver.onReceive
响应处理。看对应的 Log
文件:
1 | // 1. AMS.broadcastIntent 发送广播 |
示例
静态注册广播接收器
BroadCastReceiver
子类文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class BroadcastReceiverMy extends BroadcastReceiver {
private static final String TAG = "XMT:BroadcastReceiverMy";
public void onReceive(Context context, Intent intent) {
if (intent != null){
// broadcast ANR 60s
//Log.d(TAG, "BroadcastReceiverMy: sleep...");
//try{
// Thread.sleep(65*1000);
//} catch(Exception e){}
String value = intent.getStringExtra("key");
Log.d(TAG, "BroadcastReceiverMy: onReceive = " + value);
}
}
}在
AndroidManifest.xml
中声明1
2
3
4
5
6
7
8
9
10
11<receiver
android:name=".ipc.BroadcastReceiverMy"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="broadcast.service.normal"></action>
</intent-filter>
<intent-filter>
<action android:name="broadcast.service.order"></action>
</intent-filter>
</receiver>
动态注册广播接收器
动态注册和取消注册广播必须在生命周期中成对出现,比如在 onCreate/onDestroy, onResume/onPause, onStart/onStop
对应出现。
1 | private boolean registeredOrder; |
广播发送
1 | // 发送有序广播,并包含回调广播接收器 |
小结
广播接收器静态注册过程
静态注册的广播接收器,是开机时 PMS
启动后解析扫描所有包时注册的,保存在每个包的 PackageParser.Package.receivers
中,系统 PMS.mReceivers
会全局保存所有静态注册的广播接收器。 AMS
中并不会保存静态注册的广播接收器信息,而是直接向 PMS
中查询。
AMS
中存储的广播相关信息
- 广播队列:前台广播队列
mFgBroadcastQueue
和后台广播队列mBgBroadcastQueue
- 广播接收器哈希表
mRegisteredReceivers
:所有动态注册的广播接收器 - 广播过滤器符号表
mReceiverResolver
:所有动态注册的广播过滤器
1 | BroadcastQueue mFgBroadcastQueue; |
权限检查
广播在发送的过程中,会有大量的权限检查,比如 AMS.broadcastIntentLocked, BroadcastQueue.processNextBroadcast, BroadcastQueue.deliverToRegisteredReceiverLocked
都会反复做权限检查。
广播接收器优先级
数字越大表示优先级越高,接收有序广播时优先级高的先接收到。
静态注册广播接收器
1
2
3
4
5<receiver android:name=".smsReceiver">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>动态注册广播接收器
1
2
3
4IntentFilter intentFilter = new IntentFilter(
"android.provider.Telephony.SMS_RECEIVED");
intentFilter.setPriority(1000);
registerReceiver(broadcastReceiver, intentFilter);
广播接收器响应
广播接收器回调 onReceive
触发响应的位置
- 静态注册的广播接收器
ActivityThread.handleReceiver
中使用反射,触发onReceive
方法。静态注册的广播接收器并不会对应ReceiverDispatcher
。 - 动态注册的广播接收器
动态注册的广播接收器对应一个ReceiverDispatcher
,通过它找到LoadedApk.Args
,在Runnable.run
中触发onReceive
方法。
前后台广播及 ANR
广播发送时,通过 Intent
是否设置 FLAG_RECEIVER_FOREGROUND
来区分,如果设置了表示前台广播,对应 ANR
超时为 10 秒;默认为后台广播,对应超时为 60 秒。
广播的 ANR
是会出现在有序处理广播队列中,在 BroadcastQueue.processNextBroadcast
中计算的,从有序队列 mOrderedBroadcasts
取出有效广播后开始计时,直到有序广播匹配的所有广播接收器包括有序广播的回调广播接收器,全部处理完毕才会取消计时。具体可以看“广播 ANR
” 这一节的 Log
分析。
注意:如果广播接收器的 onReceive
是在主线程中处理,同时还会受到 Activity
超时 5 秒产生 ANR
的限制!也就是说等不到 60 秒,可能 Activity
就出现 ANR
了。如果 onReceive
有耗时任务,请新开线程处理。
有序广播
- 可以被终止
优先级高的广播接收者接收到后,可以通过abortBroadcast
来终止继续向下传递广播。 - 可以有回调
BroadCastReceiver
广播接收器回调,发送有序广播时,可以同时指定一个BroadCastReceiver
回调;当有序广播全部被执行完后,会触发这个回调。也就是说有序广播,在顺序接收过程中,每个广播接收者都可以在回调BroadCastReceiver
中向发送者写回数据。
广播发送过程
- 整个广播发送及响应过程都是异步的,大量使用
Handler
异步处理 - 源码中可以看到大量
synchronized
关键字,而且大多使用AMS
作为对象锁,所以系统处理广播的过程有大量同步过程
并行处理和有序处理
mParallelBroadcasts
广播队列
存储的广播为普通广播(对应的广播接收器是动态注册的),该队列的所有广播都是并行处理的,在processNextBroadcast
中一次全部处理完。mOrderedBroadcasts
广播队列
存储的广播为有序广播和普通广播(对应的广播接收器时静态注册的)该队列的所有广播都是有序处理的,在processNextBroadcast
中从队列中取出一个有效广播,处理完一个广播接收器后,会触发AMS.finishReceiver
结束整个广播过程。但是finishReceiver
方法每次都会调用BroadcastQueue.finishReceiverLocked
检查mOrderedBroadcasts
是否全部处理完毕。如果没有,调用processNextBroadcast
继续从有序队列中取出广播处理。具体可以参考“广播ANR
” 中的Log
分析。
进程和线程间交互
Handler
线程间异步通信LocalBroadcastManager
同一个应用相同进程内异步通信,本质上仍然是通过Handler
实现的BroadCastReceiver
进程间异步通信,不同应用不同进程间的通信,通过系统广播总线统一管理广播,用户使用很简单方便Messenger
进程间异步通信,相同应用不同进程间的通信,常用于Activity, Service
不在同一进程时使用AIDL
进程间同步通信