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


目录
相关文章
|
2月前
|
设计模式 Android开发
[Android 四大组件] --- BroadcastReceiver
[Android 四大组件] --- BroadcastReceiver
33 0
|
3月前
|
Android开发 开发者
什么是Android Jetpack,它包括哪些组件?
什么是Android Jetpack,它包括哪些组件?
42 0
|
16天前
|
存储 数据库 Android开发
构建高效安卓应用:采用Jetpack架构组件优化用户体验
【4月更文挑战第12天】 在当今快速发展的数字时代,Android 应用程序的流畅性与响应速度对用户满意度至关重要。为提高应用性能并降低维护成本,开发者需寻求先进的技术解决方案。本文将探讨如何利用 Android Jetpack 中的架构组件 — 如 LiveData、ViewModel 和 Room — 来构建高质量的安卓应用。通过具体实施案例分析,我们将展示这些组件如何协同工作以实现数据持久化、界面与逻辑分离,以及确保数据的即时更新,从而优化用户体验并提升应用的可维护性和可测试性。
|
2月前
|
数据可视化 Android开发
[Android 四大组件] --- Service
[Android 四大组件] --- Service
24 0
|
2月前
|
Android开发
[Android 四大组件] --- Activity
[Android 四大组件] --- Activity
22 1
|
2月前
|
存储 数据库 Android开发
安卓四大组件是什么?
安卓四大组件是什么?
|
3月前
|
数据库 Android开发 开发者
Android基础知识:什么是Android应用的四大组件?
Android基础知识:什么是Android应用的四大组件?
62 1
|
7天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
25 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
29天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
14 0
|
3天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。