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类型的对象,用于保存IIntentReceiverIBinderReceiverList(实际上这个类是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实例中封装了ReceiverDispatcherReceiverDispatcher封装了BroadcastReceiver receiver实例
在这里插入图片描述

三、流程图

在这里插入图片描述

四、结论

(1)广播注册过程中需要将BroadcastReceiver实例封装成一个Binder对象,再跨进程调用AMSregisterReceiver方法
(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

posted @ 2021-06-29 16:45  asjhan  阅读(26)  评论(0编辑  收藏  举报  来源