Android 四大组件之一:BroadCastReceiver动态注册广播流程

简介: Android 四大组件之一:BroadCastReceiver动态注册广播流程

动态注册广播流程

动态注册广播流程

1.在Activity中动态注册广播时,调用registerReceiver方法,会调用到ContextWrapper的registerReceiver方法:

2.这个方法内部,mBase其实就是一个ContextImpl类型的对象,所以会执行ContextImpl类的registerReceiver方法

经过registerReceiver重载最终会调用到registerReceiverInternal这个方法中,在这个方法中会先将scheduler先赋值为主线程的handler(上面传的scheduler为null,这个Hanlder是后面用来分发ActivityManagerService发送过的广播用的);

接下来创建一个IIntentReceiver类型的对象:如果mPackageinfo不为空调用mPackageInfo.getReceiverDispatcher创建,为空调用LoadedApk.ReceiverDispatcher创建(mPackageInfo是LoadedApk类型的对象),其实IIntentReceiver就是LoadedApk.ReceiverDispatcher.InnerReceiver类型的对象,由于InnerReceiver是继承IIntentReceiver.Stub,所以这个InnerReceiver类型的对象其实就是一个Binder,它后面作为app进程和system_server进程进行通信的桥梁(AMS在收到相应的广播时,就是通过这个Binder对象来通知MainActivity来接收的。)。mPackageInfo是LoadedApk类型的对象,接着先分析LoadedApk类的getReceiverDispatcher方法的具体实现:

通过LoadedApk的getReceiverDispatcher方法,可以看出,它里面调用了LoadedApk.ReceiverDispatcher的构造方法,从上面贴出的代码可以看到,这个构造方法里面创建了一个InnerReceiver的对象,并将这个对象赋值给mIIntentReceiver这个变量,后面通过getIIntentReceiver方法获取的就是这个InnerReceiver类型的对象。

小结:

1.在新建广播接收发布器ReceiverDispatcher时,会在构造函数里面创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,可以通过ReceiverDispatcher.getIIntentReceiver函数来获得,获得后就会把它传给ActivityManagerService,以便接收广播。

2.在ReceiverDispatcher类的构造函数中,还会把传进来的Handle类型的参数activityThread保存下来,以便后面在分发广播的时候使用。

LockedApk中有一个mReceivers对象,以Context为key, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>为value。每一个Context都有一个这样的hashmap,key就是receiver,value是ReceiverDispatcher。每一个receiver都对应一个ReceiverDispatcher。

总结:就是创建了一个RD对象(ReceiverDispatcher为了之后AMS和收到广播之后远程调用和可以通过handler插入到消息队列中去处理),这个是在LockedApk中创建的,首先LockedApk有一个mReceivers属性,这个属性里面存放的是key是Context,value是这个上下文中所有注册了receiver的集合,key是receiver,value是RD,这个RD构造方法里面保存了hander以便收到消息之后添加到消息队列中,RD里面有一个静态类InnerReceiver继承自这个类IIntentReceiver.Stub是个代理所以可以通过他进行AMS到Rd中的通信

3.在完成了mPackageInfo.getReceiverDispatcher()创建InnerReceiver对象后,继续调用了AMS的registerReceiver方法

拆开进行分析:

(1)首先获取注册的进程信息

(2)接着迭代filter的actions进行调用getStickLocked函数查找一下有没有对应的sticky intent列表存在。

sticky intent列表:我们在最后一次调用sendStickyBroadcast函数来发送某个Action类型的广播时,系统会把代表这个广播的Intent保存下来,这样,后来调用registerReceiver来注册相同Action类型的广播接收器,就会得到这个最后发出的广播。这就是为什么叫做Sticky Intent了,这个最后发出的广播虽然被处理完了,但是仍然被粘住在ActivityManagerService中,以便下一个注册相应Action类型的广播接收器还能继承处理。

(3)接下来把广播接收器receiver保存一个ReceiverList列表中,这个列表的宿主进程是rl.app,在ActivityManagerService中,用一个进程记录块来表示这个应用程序进程,它里面有一个列表receivers,专门用来保存这个进程注册的广播接收器。接着,又把这个ReceiverList列表以receiver为Key值保存在ActivityManagerService的成员变量mRegisteredReceivers中,这些都是为了方便在收到广播时,快速找到对应的广播接收器的。

(4)上面只是把广播接收器receiver保存起来了,但是还没有把它和filter关联起来,这里就创建一个BroadcastFilter来把广播接收器列表rl和filter关联起来,然后保存在ActivityManagerService中的成员变量mReceiverResolver中去。

这个方法内部,首先获取了最后一个粘性消息(如果有多个action,取最后一个action的最后一次粘性消息,在最后进行返回这个粘性消息。如果注册的receiver传入的是null只传入了actions,那么会直接返回这个最后的粘性消息),接着通过AMS的本地集合变量mRegisteredReceivers通过receiver(key)获取rl(ReceiverList就是value)获取不到进行创建rl,rl构造方法中传入了进程记录块(callerApp)和Binder调用者的uid和pid,还有receiver。创建完后之后把当前rl添加到rl的进程记录块里面的receivers中,然后将receiver为key,rl为value保存添加到mRegisteredReceivers中;

receivers保存完之后开始保存filter,创建一个BroadcastFilter(将filter和rl传入),接着把bf添加到rl中,并把bf添加到AMS本地集合mReceiverResolver中。

这样注册过程就完成了。

广播发送流程

广播的发送者将广播发送到ActivityManagerService,ActivityManagerService接收到这个广播以后,就会在自己的注册中心查看有哪些广播接收器订阅了该广播,然后把这个广播逐一发送到这些广播接收器中,但是ActivityManagerService并不等待广播接收器处理这些广播就返回了,因此,广播的发送和处理是异步的。概括来说,广播的发送路径就是从发送者到ActivityManagerService,再从ActivityManagerService到接收者,这中间的两个过程都是通过Binder进程间通信机制来完成的

1.在Activity中发送广播,sendBroadCast方法其实是调用了ContextWrapper的sendBroadcast方法,ContextWrapper类中的sendBroadcast方法,其实也是调用了ContextImpl的sendBroadcast方法

这里的resolvedType表示这个Intent的MIME类型

  1. 这个方法中,通过进程间通信的的方式,调用了system_server进程的ActivityManagerService的broadcastIntent方法

在里面调用了broadcastIntentLocked方法,拆开进行分析:

3. 首先对Intent进行了处理,如果broadCastReceiver所在进程死亡那么将不会收到该广播

  1. 如果广播没有指定特性的接受者的话那么会调用到mReceiverResolver.queryIntent()这个方法中,在上面注册的时候BroadcastFilter实例保存在了ActivityManagerService的成员变量mReceiverResolver中,这个BroadcastFilter实例包含了我们所注册的广播接收器,这里就通过mReceiverResolver.queryIntent函数将这个BroadcastFilter实例取回来。由于注册一个广播类型的接收器可能有多个,所以这里把所有符合条件的的BroadcastFilter实例放在一个List中,然后返回来。

  2. 这里是查看一下这个intent的Intent.FLAG_RECEIVER_REPLACE_PENDING位有没有设置,如果设置了的话,AMS就会在当前的系统中查看有没有相同的intent还未被处理,如果有的话,就有当前这个新的intent来替换旧的intent。

  3. 这个if语句首先创建一个广播记录块BroadcastRecord,里面记录了这个广播是由谁发出的以及要发给谁等相关信息。由于前面得到的replacePending变量为false,这里得到的replaced变量的值也为false,于是执行下面的if语句,没有替换时,才需要将新的广播加入到BroadcastQueue.mParallelBroadcasts队列中,等待进一步处理;进一步处理的操作由函数scheduleBroadcastsLocked进行。处理完成之后会把NR重置

上面分析的是动态广播(NR>0因为是在动态regist的时候会把bf添加到mReceiverResolver这个AMS本地变量中,如果是静态的是查询不到mReceiverResolver变量里面的bf),接下来分析静态注册的广播,静态注册的广播receivers不为null。

小结:

1.先获取动态注册的广播和静态注册的广播,并将这些广播分别存储到两个不同的列表中

2.判断是否是发送的普通广播,如果是,并且动态注册的广播,则先将动态注册的广播发送, 走后续的广播发送流程,接着判断是否有静态广播,如果有静态广播,则发送静态广播。(从源码代码逻辑可以知道,普通广播的发送,是动态广播优先静态广播发送)如果发送的是有序广播,则会将第一步的两个列表合并到receivers列表中,并按照优先级对广播进行排序,具体的排序规则是,优先级高的排前面,对于相同优先级的,动态优先静态,对于优先级相同,广播类型相同,如果都是动态广播类型,则先注册的优先后注册的,对于都是静态广播类型,则先扫描的由于后扫描的。

3.将广播将第二步的广播列表,添加到BroadcastQueue的mParallelBroadcasts或者mOrderedBroadcasts中,如果广播按照普通方式发送,则将广播存储在mParallelBroadcasts列表中,如果是静态广播存储在mOrderedBroadcasts列表中,如果发送方式是有序广播,则将所有的广播(不管是静态注册的还是动态注册的)都存储在mOrderedBroadcasts列表中。

  1. 动态广播:通过调用BroadcastQueue的enqueueOrderedBroadcastLocked方法将发送的广播存储在BroadcastQueue的mParallelBroadcasts列表中,然后通过scheduleBroadcastsLocked方法发送广播。

这里的mBroadcastsScheduled表示ActivityManagerService当前是不是正在处理其它广播,如果是的话,这里就先不处理直接返回了,保证所有广播串行处理。注意这里处理广播的方式,它是通过消息循环来处理,每当AMS接收到一个广播时,它就把这个广播放进自己的消息队列去就完事了,根本不管这个广播后续是处理的,因此,这里我们可以看出广播的发送和处理是异步的。这里的成员变量mHandler是一个在ActivityManagerService内部定义的Handler类变量,通过它的sendEmptyMessage函数把一个类型为BROADCAST_INTENT_MSG的空消息放进AMS的消息队列中去。这里的空消息是指这个消息除了有类型信息之外,没有任何其它额外的信息,因为前面已经把要处理的广播信息都保存在mParcelBroadcasts中了,等处理这个消息时,从mParcelBroadcasts就可以读回相关的广播信息了,因此,这里不需要把广播信息再放在消息内容中。

  1. 接下来分析processNextBroadcast函数,这个函数有点长进行分步分析

(8.1)这里传进来的参数fromMsg为true,于是把mBroadcastScheduled重新设为false,这样,下一个广播就能进入到消息队列中进行处理了。

(8.2)无序广播,并调用deliverToRegisteredReceiverLocked方法发送无序广播

(8.3)如果是发送的有序广播,则判断广播所属的进程是否存在,并且进程未被kill,则执行processCurBroadcastLocked处理后续的流程,如果广播所属的进程不存在,则新建一个进程,并重复广播的发送过程,这些情况的后续流程。如果只是发送普通股广播,并且广播就所属当前进程,那么直接执行deliverToRegisteredReceiverLocked方法

(8.4)deliverToRegisteredReceiverLocked方法中,又会调用到performReceiveLocked方法,performReceiveLocked方法中,会调用app.thread.scheduleRegisteredReceiver方法,app.thread其实是ApplicationThread类型的对象,这个在之前就分析过,代码的执行又回到了ApplicationThread类中

前面bt中app属性保存的就是注册广播的进程,receiver属性保存的是对应的所有广播接收器。

(8.5)这个receiver就是上面ASM注册广播的时候传进来的LockedApk中的InnerReceiver

(8.6)在注册的时候rd会在构造方法中创建InnerReceiver,并把当前rd传入到InnerReceiver的构造函数中,并用弱引用保存rd

(8.7)把消息放在消息队列中,然后就返回了,这个消息最终会在传进来的Runnable类型的参数的run成员函数中进行处理。

(8.8)分发到了接收器的onReceiver方法中

参考链接:

https://blog.csdn.net/luoshengyang/article/details/6730748

https://blog.csdn.net/luoshengyang/article/details/6737352


目录
相关文章
|
10天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
1月前
|
存储 Android开发 开发者
深入理解安卓应用开发的核心组件
【10月更文挑战第8天】探索Android应用开发的精髓,本文带你了解安卓核心组件的奥秘,包括Activity、Service、BroadcastReceiver和ContentProvider。我们将通过代码示例,揭示这些组件如何协同工作,构建出功能强大且响应迅速的应用程序。无论你是初学者还是资深开发者,这篇文章都将为你提供新的视角和深度知识。
|
1月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
86 0
|
30天前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
76 6
|
28天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
1月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
26 3
|
1月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
24 2
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
39 3
|
1月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
1月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
18 0