BroadcastReceiver注册过程
这篇我们来阅读Framework源码BroadcastReceiver的注册过程,阅读源码的过程枯燥的,所以在阅读源码时要给自己提问,要多想段代码是做什么的,然后基于猜测去寻找答案。
一、阅读环境
(1)AndroidXRef(在线源码)
(2)Android 9.0
二、阅读过程
我们阅读的是动态注册广播的过程,对于静态注册的过程不在此次阅读范围之内。源代码以图片展现,最好使用电脑端观看。
(1)首先我们要寻找阅读的起点,我们知道注册动态广播的方式是registerReceiver(BroadcastReceiver,IntentFilter)
,那么以此为起点一步步阅读。那么我们通过在线源码先来查看Activity
(因为在Activity中可以调用注册动态广播的方法)。
a. 在Activity
中不存在此方法,发现没有此方法,那么我们应该寻找它的父类
b. 在ContextWrapper
类中找到了此方法,发现调用了mBase.registerReceiver
,mBase
实现类是ContextImpl
c. 在ContextImpl
类中,registerReceiver
方法最终调用了registerReceiverInternal
方法
d. 阅读registerReceiverInternal
方法。
e. 那么这里产生两个分支,一个是mPackageInfo.getReceiverDispatcher
,另一个是ActivityManager.getService().registerReceiver
,那么我们先阅读哪一个呢,若是想弄清楚receiver怎么封装的就要查看getReceiverDispatcher
方法,这里我们都去阅读,读者可以按需要去看。
(2)阅读ActivityManager.getService().registerReceive
过程。
a. 如图下所示,IApplicationThread
是调用者app,getRecordForAppLocked
方法会返回调用者app的进程信息ProcessRecord
。在紧接着的if判断中,判断当前app是否为系统app,正在运行的进程中是否存在,若不存在则抛出异常。callerApp.pkgList
是正在运行的app列表信息
b. mUserController.handleIncomingUser
方法主要是处理多用户相关的操作用于校验。接下来就是遍历广播接收器的IntentFilter的action列表,并从粘性广播列表中匹配出对应的广播,添加至List中
c. 遍历粘性广播,判断当前app是否是一个instant app,若是instant app那么就判断是否设置标志位FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
,若没有设置,那么instant app是接收不到广播的;接下来就是filter.match
方法,匹配正在注册广播接收器的filter
和粘性广播的是否相同,若相同则添加至allSticky
list列表中
d. mRegisteredReceivers
是一个map
类型的对象,用于保存IIntentReceiver
的IBinder
和ReceiverList(实际上这个类是ArrayList<BroadcastFilter>的子类,这个类的实际作用是,保存IIntentReceiver[InnerReceiver内部间接封装BoradcastReceiver]、BroadcastFilter[父类为IntentFilter]、ProcessRecord[相当于进程信息]、uid、pid、userId等信息)
之间的映射;系统中已经注册的广播接收器会保存在HashMap<IBinder,ReceiverList> mRegisteredReceivers
中;app中已经注册的广播接收器会保存在ProcessRecord(相当于代码中的rl.app)
进程信息中;rl.app.receivers.add
的操作是注册广播接收器到进程
e. 创建BroadcastFilter(bf)
实例封装了IntentFilter(filter)
,并添加到ReceiverList(rl)
对象中,那么在ReceiverList(rl)
实例中就保存有IIntentReceiver(receiver)
和BroadcastFilter(bf)
。最后若存在粘性广播,则发送广播至注册的广播接收器。注:在发送广播的过程中可以通过mReceiverResolver
对象可以获取到发送广播(Intent)对应的动态广播接收器。
(3)阅读mPackageInfo.getReceiverDispatcher
方法
a.在getReceiverDispatcher
方法中,判断mReceiver.get(context)
含义是在同一个Context
实例中是否存在一个或多个广播接收器,判断rd == null 或者说 map.get(r) == null
含义是在广播接收器map
集合中是否已经注册过这个实例(BroadcastReceiver r)
的广播接收器; mReceivers
类型为ArrayMap<Context,ArrayMap<BroadcastReceiver,ReceiverDispatcher>>
表示的含义:在app中,同一个Context
实例保存有N个广播接收器
b. ReceiverDispatcher
构造方法中,InnerReceiver
继承自IIntentReceiver.Stub
,所以说InnerReceiver
是一个Binder
对象,在这个Binder
实例中封装了ReceiverDispatcher
;ReceiverDispatcher
封装了BroadcastReceiver receiver
实例
三、流程图
四、结论
(1)广播注册过程中需要将BroadcastReceiver
实例封装成一个Binder
对象,再跨进程调用AMS
的registerReceiver
方法
(2)广播的注册过程中会查询粘性广播列表是否和注册的广播接收器的IntentFilter
相匹配,若匹配,之后会发送广播给这个接收器
(3)在注册中通过getRecordForAppLocked
方法获取到app的进程信息ProcessRecord
,通过ProcessRecord.ArraySet<ReceiverList>.add(rl)
注册广播接收器到进程中
(4)ReceiverList
的父类是ArrayList<BroadcastFilter>
,通过rl.add(bf)
添加广播的过滤器
(5)BroadcastFilter
的父类是IntentFilter
,封装广播接收器的IntentFilter
等信息
asjhan for Android reverse