戳蓝字"牛晓伟"关注我哦!
用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章,技术文章也可以有温度。
本文摘要
从本文开始介绍广播 相关的内容,本文主要介绍广播机制,广播机制的原理,广播的分类,为啥要有超时机制,为啥要有延迟机制,无序广播的发送接收流程,有序广播的发送接收流程 ,关于广播的所有知识都可以在本文找到。
注:AMS是ActivityManagerService的简称
本文大纲
1. 广播机制
大家好,我是广播机制 ,广播机制可以理解为全局的事件通知机制 ,用大白话讲就是所有的App进程之间或者App进程内都可以通过该机制把事件发送给事件的监听者 ,这里的事件监听者 就是鼎鼎有名的BroadcastReceiver 组件,事件的监听者可以有多个。该机制的好处是解除了代码之间的耦合性 、提升开发效率。BroadcastReceiver其实不需要关心事件是由哪个进程发出来的,它只需要注册自己及对应的广播即可,至于何时有广播它也不需要关心,只需要被动接收广播数据即可,这样大大的降低了代码之间的耦合性。
为了对广播有一个更直观的感受,我特意写了一段伪代码:
java
//如下代码注册一个广播接收者
IntentFilter intentFilter = new IntentFilter("com.niu.broadcast.demo");
registerReceiver(new BroadcastReceiver(),intentFilter);
private BroadcastReceiver br = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
接收到相应广播并且进行处理的代码······
}
};
//如下代码发送 com.niu.broadcast.demo 类型的广播,上面的BroadcastReceiver对象就会收到该广播信息
Intent intent = new Intent("com.niu.broadcast.demo");
sendOrderedBroadcast(intent,null);
上面伪代码,展示了一个广播接收者注册了对应的广播后,发送者发送该广播后,广播接收者就会收到该广播信息。上面的代码是不是简单到令人发指的地步啊。大家肯定能猜出来这肯定是我广播机制作为底层做了很多很多的事情,那就跟随我一步一步来揭开底层的神秘面纱吧。
2. 广播机制原理
在介绍广播机制原理之前,我同样延续以前的由浅入深的老传统,先从最核心的"内核"开始,只有先把机制的"内核"搞清楚,才能对机制有一个更深入的理解。
2.1 "内核"
广播机制的"内核"就是设计模式中的观察者模式 ,观察者模式又被称为发布--订阅模式,不管是发布--订阅模式还是观察者模式在现实生活中都可以找到相对应的场景,比如微信公众号中关注/订阅了某个公众号,而这个公众号只要发布了消息,订阅者就会收到该消息,当然订阅者可以有很多。我特意绘制了一幅图来展示该设计模式:
图解
Subject的作用是持有很多的Observer,发布者发布消息需要通过它来发布。
Observer的作用就是把自己注册在Subject中,这样当有消息时就能够收到对应消息了,就犹如公众号的订阅者,它接收消息的过程是一个被动的过程,它不关心啥时候来消息,有消息Subject会通知它,没消息"傻傻等着"。
发布者的作用就是发布消息,发布的消息需要经过Subject传递给所有的Observer。
介绍了广播机制的"内核",那我就来介绍下广播机制的原理。
2.2 原理
还是先请大家来看一幅图,该图展示了广播机制的原理:
图解
因为我广播机制 是一个全局的事件通知机制 ,每个App进程都可以存在一个或者多个广播接收者 (BroadcastReceiver),而广播接收者就是观察者模式中的Observer,只不过广播接收者是存在于各个App进程内。
广播分发中心 就是观察者模式中的Subject的作用,但是广播分发中心的功能可是比Subject复杂的多的多的多了。正如它的名字它会把广播分发给广播接收者,广播分发中心是位于systemserver进程,广播分发中心是由AMS 、BroadcastQueue 、BroadcastDispatcher等几个关键类组成的。
广播发送者就是观察者模式中的发布者的作用,同样广播发送者可以位于App进程,也可以位于systemserver进程,广播发送者的作用就是发送广播。
上图只是展示了广播机制的关键参与方,以及发送和接收广播的流程。广播机制可是有很多复杂的事情需要考虑比如广播接收者、广播分发中心、广播发送者一般都是位于不同的进程,那这时候需要使用binder通信 ;又比如广播分发中心是如何鉴别广播接收者的;Android系统每时每刻都会有广播的发送,那该如何保证广播消息的即时响应;发送的广播各种各样,并且广播接收者也多种多样,那如何定义一套统一的接口或机制保证可以在所有发送者和接收者上都能通用等等各种事情。那我就从广播接收者 、广播发送者 、广播分发中心再来揭开广播机制的原理吧。
3. 广播接收者
广播接收者就是BroadcastReceiver ,只要作为一个广播接收者就需要自定义自己的类,这个类需要继承BroadcastReceiver,重写BroadcastReceiver的onReceive方法,在该方法中处理收到的广播信息。
而广播接收者是被划分为动态广播接收者 和静态广播接收者两种,那我就来介绍下这两种。
3.1 动态广播接收者
动态广播接收者 就是指该BroadcastReceiver是需要通过代码的方式进行注册和注销,它的生命周期与所在的组件相同,比如在Activity中注册了该BroadcastReceiver,在Activity销毁的时候就需要注销该BroadcastReceiver。它的特点是灵活。
既然谈到了注册和注销,就像观察者模式中的Observer一样,要想接收消息就需要在Subject中注册自己,而动态广播接收者同样也需要进行注册的流程,那就来谈谈注册。
3.1.1 注册
先看一段注册动态广播接收者的代码:
java
//"com.niu.broadcast.demo"代表BroadcastReceiver关心的广播
IntentFilter intentFilter = new IntentFilter("com.niu.broadcast.demo");
//XXXBroadcastReceiver代表一个广播接收者类
registerReceiver(new XXXBroadcastReceiver(),intentFilter);
如上代码注册一个BroadcastReceiver是不是很简单,每一个广播都对应自己的action (也就是一个字符串),注册一个BroadcastReceiver就是调用Context对象的registerReceiver方法把它和它关心的广播作为参数即可。
但是你可别被上面的代码欺骗了,真正注册的可不是BroadcastReceiver对象,那就听我来细细介绍注册 ,要想把BroadcastReceiver的注册 讲明白,那需要先把注册到哪 、注册谁 、如何注册这几件事情讲明白。
注册到哪
在上面介绍广播机制的原理图中提到过,广播接收者BroadcastReceiver一般都是位于App进程内的,而广播分发中心是位于systemserver进程的,而广播分发中心的首要作用就是收集所有的"BroadcastReceiver" ,因此就是把"BroadcastReceiver"注册到分发到广播分发中心,不知道细心的你们有没有发现BroadcastReceiver可是加了引号 ,加引号就是代表并不是BroadcastReceiver对象被注册到广播分发中心,那到底是注册谁呢?
注册谁
广播分发中心对于注册的对象 可是有两个要求的,这个要求就是具有反向查找的能力 和唯一性 。先来解释下反向查找 ,说人话就是该对象再次被传递到App进程后 ,可以通过该对象找到对应的BroadcastReceiver对象 。而唯一性 指一个App进程内多次注册同一对象,在广播分发中心依然知道注册的是同一对象。
而App进程内的BroadcastReceiver对象,它可是不具备以上两点的,因为它的类型只是一个普通的类而已,而"谁"满足以上两个要求呢?
答案就是Binder对象 ,还记得在Activity与ActivityRecord"互认"这篇文章中介绍过ActivityRecord与Activity"互认"是使用了Token 对象,而Token类继承了Binder类。既然知道了需要注册一个Binder对象 ,那就来看下是该Binder对象是什么类型的,请看如下代码:(关于为啥是Binder对象可以看Activity与ActivityRecord"互认"此文)
scala
//下面类位于LoadedApk.java
//InnerReceiver类继承了IIntentReceiver.Stub,而IIntentReceiver.Stub继承了Binder类
final static class InnerReceiver extends IIntentReceiver.Stub {
//该方法会进行广播的分发
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
省略代码······
}
}
//IIntentReceiver.Stub是一个抽象类继承了Binder类并且实现了IIntentReceiver接口
abstract class Stub extends android.os.Binder implements android.content.IIntentReceiver{
@Override public void performReceive(android.content.Intent intent, int resultCode, java.lang.String data, android.os.Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws android.os.RemoteException
{
}
}
如上代码,InnerReceiver 类继承了Binder类,该类是可以间接的找到BroadcastReceiver对象的 ,同时该类拥有performReceive 方法,该方法会把广播消息分发给BroadcastReceiver对象。也就是InnerReceiver对象是一个匿名的binder服务 ,它会被注册到广播分发中心 ,而当有广播消息时,InnerReceiver对象会把消息间接的分发到BroadcastReceiver对象。
如何注册
如何注册 就是调用ActivityManager的registerReceiverWithFeature方法,而该方法是一个binder调用,最终会调到AMS的相应方法,关于如何注册会在下面还会详细介绍。
小结
动态BroadcastReceiver要想接收相应的广播消息,必须先进行注册,调用Context的registerReceiver方法进行注册时,注册时最重要的参数是传递自定义BroadcastReceiver对象 和相应的广播信息 (广播信息放入IntentFilter对象),当然有一些广播还需要权限。表面上看着是在注册自定义的BroadcastReceiver对象,但其实注册的是InnerReceiver对象 。而InnerReceiver对象 和相应的广播等信息 是会注册到广播分发中心的。
3.2 静态广播接收者
静态广播接受者 就是指该BroadcastReceiver是只在AndroidManifest文件中声明即可,如下例子:
ini
<receiver android:name=".MyReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.niu.broadcast.demo"/>
</intent-filter>
</receiver>
静态广播接收者没有动态广播接收者灵活,但是它也有它自己的优势,那就是不需要App进程"活"着,啥意思呢?就是说静态广播接收者即使对应的App进程没有存活,也依然会被系统拉活,进而接收广播消息。而动态广播接收者对应的App进程必须存活。同时也正因为此特性,并不是所有的App都可以使用此特性,因为如果使用了此特性就可以悄悄的把死掉的App进程拉活,因此一般只有系统App才可以使用此特性。
3.3 小结
广播接收者就是一个BroadcastReceiver类,而广播接收者又可以分为动态广播接收者 和静态广播接收者 ,不管是什么类型的广播接收者,要想收到广播,就需要注册自己感兴趣的广播,当然可以注册多个广播 ,而每一个广播都有对应的action ,而这个action就是一个字符串 ,它当作广播的key值,因此注册多个广播就是把广播的action存放在IntentFilter对象中或者配置在intent-filter标签中。
4. 广播发送者
广播有接收者,那自然也有发送者,发送一个广播非常简单,如下例子:
java
//如下代码发送action为com.niu.broadcast.demo 类型的广播
Intent intent = new Intent("com.niu.broadcast.demo");
//下面代码为intent增加参数
intent.putExtra("xxxx","xxxxxx");
sendBroadcast(intent);
如上代码,发送一个action为com.niu.broadcast.demo的广播,并且还携带了参数。不管是广播对应的action信息还是参数都存放在Intent对象中,而Intent对象会通过binder通信到达AMS,而AMS开始执行广播分发的过程,在下面会介绍广播分发的过程。
其实广播发送者发送的广播是可以按是否有序 、按是否是系统 、按是否是后台进行划分的,那我就依据以上划分类来介绍下不同类型的广播。
4.1 按是否有序划分
广播按是否有序划分为有序广播 和无序广播,为了展示它俩的区别,我特意绘制了一幅图:
图解
左图展示的是有序广播的发送流程,广播分发中心会先把广播发送给第一个接收者,第一个接收者处理完毕后会给广播分发中心一个反馈;接着把广播发送给第二个接收者,同样第二个接收者处理完毕也会给广播分发中心一个反馈。依照此顺序直达发完为止。
有图展示的是无序广播的发送流程,广播分发中心会把广播分发给所有的接收者,而不需要等待接收者的反馈。
由上可知,广播发送者发送的有序广播 和无序广播 的区别在于广播接收者接收广播的顺序,前者是一个串行的概念,广播接收者需要一个一个的接收,只有前一个接收者处理完毕后,才会把广播发送给下一个接收者;而后者是一个并行的概念,广播接收者可以基本同时接收广播。
正因为有序广播是按照串行来发送广播给接收者的,因此会出现一个问题就是一个接收者处理广播时间耗时,那就会影响后面的接收者,因此针对有序广播增加了超时 、延迟等机制,后面会介绍到。而无需广播不需要这些机制。
广播发送者可以调用Context的sendBroadcast 和sendOrderedBroadcast方法来发送无序广播和有序广播。
4.2 按是否是系统划分
广播按是否是系统划分为系统广播 和普通广播 。而该划分是针对广播发送者的,
系统广播 就是只有系统的一些App或者systemserver进程才能发送的广播,比如android.intent.action.SCREEN_ON、android.intent.action.SCREEN_OFF等。
普通广播就是普通App发送的广播,普通App可是不能发送系统定义的广播,否则会抛异常。
4.3 按是否是后台划分
广播按是否是后台划分为后台广播 和前台广播,这里的前台、后台可不是大家想的App处于前后台的概念,这里的前台、后台指的是时间。
前台广播 指一个广播接收者处理广播的时间最多10s ,否则按超时 处理。后台广播 指一个广播接收者处理广播的时间是60s ,否则同样按超时处理。
上面介绍过只有有序广播 有超时机制,可以认为前台广播和后台广播是针对有序广播的再次划分。
4.4 小结
广播根据以下进行划分:
- 按是否有序划分为:有序广播和无序广播
- 按是否是系统划分为:系统广播和普通广播
- 按是否是后台划分为:后台广播和前台广播,不管是后台广播还是前台广播都是针对有序广播的再次划分,不会存在无序的前台或后台广播
当然上面的广播之间还可以进行组合,比如系统有序后台广播、普通无序前台广播、系统无序广播、普通无序广播
5. 广播分发中心
广播分发中心 可是广播机制中最为重要的部分,广播分发中心是由AMS 、BroadcastQueue 、BroadcastDispatcher 等几个关键类组成的,动态广播接收者都是注册在广播分发中心 的,广播也是由广播分发中心 发送给所有的广播接收者的。因此没有广播分发中心 也就没有广播机制,广播分发中心 设计的好坏直接决定了广播分发的效率。而广播分发中心又可以分为注册模块 和分发广播模块 这俩模块,那就从这两个模块入手来揭开广播分发中心的原理吧。
5.1 注册模块
前面提到过动态BroadcastReceiver要想接收广播的话,需要进行注册,注册模块 所做的事情就是提供动态BroadcastReceiver注册的能力,以及保存注册信息 ,保存下来的注册信息供发送广播时使用 。那我就来介绍下注册模块是都提供了哪些注册能力,注册信息都有哪些,以及如何保存注册信息的。那就先从注册开始吧。
5.1.1 注册
注册 是注册模块的首要功能,而动态BroadcastReceiver要想注册的话就需要调用ActivityManager的registerReceiverWithFeature 方法,而该方法是一个binder调用,最终调用到AMS的registerReceiverWithFeature方法,下面列出了该方法的主要参数:
参数 | 说明 |
---|---|
caller:IApplicationThread | AMS通过caller把消息发送给App进程 |
callerPackage:String | 注册广播接收器的包名 |
receiver:IIntentReceiver | receiver它被注册到广播分发中心 |
filter:IntentFilter | filter存放了注册的广播 |
requiredPermission:String | 有些广播是需要权限的 |
其中receiver是类型为IIntentReceiver的匿名binder服务,在App进程中它是Binder类型 的,而被传递到AMS后,它变为BinderProxy类型 (在此文中介绍了IBinder对象在进程之间传递过程中的互相转换),receiver就犹如App进程中BroadcastReceiver的一个"代理人"。
注册BroadcastReceiver时,还需要传递该BroadcastReceiver"关心"的广播,而这些广播信息是存放在类型为IntentFilter的filter对象中的。而有些广播是需要权限的,相应的权限是放在requiredPermission中的。
5.1.2 一条注册信息的存储
既然注册了,那接下来的事情就是把这条注册信息用一个类的实例封存 起来,也就是把注册信息保存在某个类的实例中,而这个类就是ReceiverList类,下面是该类的类图:
图解
ReceiverList
该类的主要作用就是把注册的信息都存储起来,下面列出了该类的几个关键属性:
属性 | 说明 |
---|---|
receiver:IIntentReceiver | receiver可以理解为是BroadcastReceiver的"代理人",receiver如果是App进程传递过来它的类型是BinderProxy;如果是systemserver进程传递过来它的类型是Binder |
app:ProcessRecord | app对应App进程的进程信息,如进程id、进程状态等 |
pid:int | pid代表App进程的进程id |
uid:int | uid对应App的唯一id |
大家是不是会疑惑,在注册BroadcastReceiver的时候,还传递了BroadcastReceiver"关心"的广播信息 (广播信息存放于IntentFilter对象),还有权限等信息,而这些信息是没有在上面属性中体现的,那这些信息是存放于何处呢?
这么重要的信息怎么能不介绍呢,那就听我细细介绍,咱们继续看上面的类图,其中ReceiverList类继承ArrayList类,并且泛型参数是BroadcastFilter 类,那ReceiverList类为啥要继承ArrayList呢?其主要原因是同一个自定义的BroadcastReceiver对象是可以被注册多次的,只不过每次的IntentFilter对象是不一样的,为了有利于大家理解,我特意写了一段伪代码:
scss
//XXXBroadcastReceiver代表一个广播接收者类
XXXBroadcastReceiver receiver = new XXXBroadcastReceiver();
//action为"com.niu.broadcast.demo"的广播,放入intentFilter
IntentFilter intentFilter = new IntentFilter("com.niu.broadcast.demo");
//action为Intent.ACTION_SCREEN_ON的广播
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
//调用registerReceiver方法进行注册
registerReceiver(receiver,intentFilter);
//action为Intent.ACTION_SCREEN_OFF的广播,放入intentFilter
IntentFilter intentFilter1 = new IntentFilter(Intent.ACTION_SCREEN_OFF);
//调用registerReceiver方法进行注册
registerReceiver(receiver,intentFilter1);
如上代码,receiver注册了两次,第一次与该receiver一起注册的广播是com.niu.broadcast.demo、Intent.ACTION_SCREEN_ON,它们被放在intentFilter对象;第二次该与该receiver一起的广播是Intent.ACTION_SCREEN_OFF,它被放在intentFilter1对象。
根据以上信息可以得出ReceiverList类为啥要继承ArrayList,因为一个自定义的BroadcastReceiver对象会存在多个IntentFilter对象 ,而BroadcastFilter类的作用就是存放广播相关信息。
BroadcastFilter
还是看上面类图,BroadcastFilter继承了IntentFilter类,该类存储了广播相关的信息,下面罗列了它的几个关键属性:
属性 | 说明 |
---|---|
receiverList:ReceiverList | receiverList存储了类型为IIntentReceiver的receiver以及进程相关信息 |
packageName:String | 包名信息 |
而广播的action信息存储在它的父类的mActions属性中,如下:
属性 | 说明 |
---|---|
mActions:ArrayList | mActions存储了所有的action |
也就是说一个BroadcastFilter对象是可以包含多个广播的 ,而通过BroadcastFilter对象的receiverList属性找到ReceiverList对象。
小结
一条注册信息是被存放于ReceiverList对象,其中它的receiver 属性是IIntentReceiver类型的,同时receiver 也是一个IBinder对象,它是远端BroadcastReceiver对象的"代理人"。而广播信息存放于BroadcastFilter对象,该对象可是被存放于ReceiverList对象中,一个ReceiverList对象可以包含多个BroadcastFilter对象。
5.1.3 所有注册信息的存储
注册模块可不是仅仅存在一条注册信息,它可是存在非常非常多的注册信息的,一条注册信息是对应一个ReceiverList对象,那非常非常多的注册信息该咋存储呢?
答案是用HashMap<IBinder, ReceiverList>来存储,它的key值是IBinder对象,它的value值就是ReceiverList对象,为啥使用IBinder对象作为key值,难道不会重复吗?
首先IBinder对象就是类型为IIntentReceiver的receiver对象,receiver就是远端BroadcastReceiver对象的"代理人"。再来解释下为啥使用IBinder对象作为key值,在此文中介绍过IBinder对象的特性,同一IBinder对象被多次传递到别的进程,传递后拿到的IBinder对象还是一个,因此基于此点使用类型为IIntentReceiver的receiver对象作为key值。
如下代码是存储所有注册信息的代码:
swift
//ActivityManagerService
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
5.1.4 如何快速检索注册信息
先说下为啥要检索注册信息,广播发送者在发送广播时,是需要根据广播的action从所有注册信息中把对该广播有"兴趣"的注册信息检索出来,进而才可以把广播信息发送给它们。
注册模块 既然把所有的注册信息存储在了HashMap<IBinder, ReceiverList>结构中,但是该结果对于根据广播信息检索注册信息却是不利的。为啥这么说呢?
上面提到过每个广播都有自己的action,这个action就是一个字符串,它作为广播的key值,因此根据action从HashMap<IBinder, ReceiverList>结构中检索注册信息是不是很麻烦啊,因为HashMap<IBinder, ReceiverList>结构它的key值是IBinder对象。
那该如何加快检索注册信息呢?
答案是使用ArrayMap<String, BroadcastFilter[]>结构,它的key值是广播的action,而value值就是BroadcastFilter[],这样就可以根据广播的action很快的找到BroadcastFilter[]。
如下是相关代码:
arduino
//IntentResolver.java
private final ArrayMap<String, F[]> mActionToFilter = new ArrayMap<String, F[]>();
在注册时,会把BroadcastFilter对象添加到ArrayMap<String, BroadcastFilter[]>结构中,如下代码:
arduino
//ActivityManagerService
public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
String callerFeatureId, String receiverId, IIntentReceiver receiver,
IntentFilter filter, String permission, int userId, int flags) {
省略代码······
//构建BroadcastFilter对象
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
exported);
//rl是ReceiverList对象
if (rl.containsFilter(filter)) {
//rl中已经存在filter,则啥也不做
} else {
//rl中不存在filter
rl.add(bf);
//把bf加入ArrayMap<String, F[]> mActionToFilter 中
mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
}
省略代码······
}
5.1.5 小结
我用如下图来做个小结吧
5.2 分发广播模块
分发广播模块 的作用就是把广播分发给它们的接收者 ,它主要是由BroadcastQueue 类和BroadcastDispatcher类组成的,它的作用是不是非常的简单啊,但是要想把分发工作做好可不是那么容易的,要关注的点非常多。
5.2.1 队列
首先大家可以思考一个问题:如果让你来做分发广播模块,首先第一步应该想到啥?
答案是队列 ,因为分发广播模块 分发的可是所有App进程及systemserver进程发送的广播,这广播的数量可是相当大的,这么多数量的广播当然得需要放到一个队列 中。而队列 是被分为有序广播队列 和无序广播队列 ,还记得在介绍广播按是否有序被分为有序广播 和无序广播 吗,而有序广播队列 就是用来存放有序广播的,无序广播队列 自然用来存放无序广播了。这里的队列 就是ArrayList类。
下面是相关代码:
swift
//BroadcastQueue.java
//无序广播队列
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
//BroadcastDispatcher.java
//有序广播队列
private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
从上面代码中大家看到不管是有序还是无序队列中都存放的是BroadcastRecord类,那它的作用是啥呢?
5.2.2 队列元素
BroadcastRecord作为队列的元素,这个类的作用主要是用来记录一个广播及其他信息,先看下该类的主要属性:
属性 | 说明 |
---|---|
intent:Intent | 广播发送者发送广播时的intent信息,广播信息以及参数都在intent对象中 |
callerApp:ProcessRecord | 广播发送者的进程信息,比如pid、进程状态等 |
callerPackage:String | 广播发送者的包名 |
callingPid:int | 广播发送者的进程id |
callerPackage:String | 广播发送者的包名 |
callingPid:int | 广播发送者的进程id |
ordered:boolean | 广播是否是有序广播 |
sticky:boolean | 广播是否是粘性广播 |
receivers:List | 广播的接收者信息,接收者既包含动态广播接收者 也包含静态广播接收者 |
delivery:int[] | 对应receivers,每个广播接收者的状态 |
duration:long[] | 对应receivers,每个广播接收者从接收到处理完广播所花的时间 |
deferred:boolean | 该BroadcastRecord是否被延迟 |
enqueueTime:long | 该BroadcastRecord对象入队列的时间,所有的BroadcastRecord是需要放入队列中,才能被发送 |
dispatchTime:long | 开始分发该广播的时间 |
timeoutExempt:boolean | 是否豁免超时机制 |
nextReceiver:int | 下一个广播接收者的索引值,因为所有的广播接收者都是放在List中的 |
anrCount:int | 发生anr的次数 |
curFilter:BroadcastFilter | 当前的动态广播接收者 |
curApp:ProcessRecord | 当前广播接收者对应的进程信息 |
上面列举了BroadcastRecord的关键属性,其中一部分是广播发送者相关信息 如callerApp、callerPackage等;一部分是广播的信息存放于intent 属性中,广播的action以及广播携带的数据都放在intent中,ordered则代表是否是有序广播;而所有的广播接收者 是存放在receivers 属性,该属性是List类型,receivers既包含动态广播接收者 也包含静态广播接收者 ,动态广播接收者对应一个BroadcastFilter对象 (上面介绍过),静态广播接收者对应ResolveInfo对象;一部分是时间相关的信息如enqueueTime入队时间,dispatchTime则代表广播开始分发时间等。
也就是一个被发送的广播就会对应一个BroadcastRecord对象 ,广播信息及广播接收者信息,接收者状态,时间等等信息都会被记录在BroadcastRecord对象中。有了这两个基础,那就跟随我看下分发广播的流程吧。
5.2.3 分发广播流程
分发广播流程其实就是一个消费者/生产者 模式,广播发送者就相当于生产者 通过AMS把构造的BroadcastRecord对象添加到有序广播队列 或者无序广播队列 ,分发广播过程就相当于消费者 ,从无序广播队列 、有序广播队列中把BroadcastRecord对象取出来进行分发,我特意绘制了一幅分发广播的过程图:
其实整个分发广播的过程要远复杂于上图,我绘制这么一幅简单图的原因是我不希望一上来就给大家带来一种恐惧感,觉得这东西好难,而是希望从简单的开始,步步剖析。
如上图,分发广播的流程是会进行多轮 ,而每一轮都是先分发所有无序广播 ,再接着分发有序广播,此轮分发完毕接着继续进行下一轮。那就从这两个分发流程开始介绍吧。
分发所有无序广播
分发所有无序广播 就是把无序广播队列 里的所有广播都分发完毕,上面提到过BroadcastQueue对象的mParallelBroadcasts 属性就是无序广播队列 。分发无序广播有一个前提就是receiver必须是动态广播接收者 ,也就是receiver必须是BroadcastFilter对象。为啥有此规定呢?
其原因是无序广播的分发它是一个不需要等待的分发过程 ,只要把无序广播分发给接收者即可,完全不需要关心接收者如何处理广播,以及需要多久来处理广播。而静态广播接收者有可能存在对应的App进程还没启动的情况,如果App进程没启动则需要等待App进程启动,这可是一个等待过程。
我也同样特意绘制了一幅图,展示了分发所有无序广播的过程:
图解
-
判断无序广播队列是否为空,不为空则进入下面第2步;否则不执行任何操作。
-
从无序广播队列 (BroadcastQueue对象的mParallelBroadcasts属性)把index = 0位置的BroadcastRecord对象取出来,并且遍历它的所有receivers,依次调用receiver对应的IApplicationThread对象的scheduleRegisteredReceiver 方法,会把receiver、intent (广播信息包含在intent中) 等信息发送给对应进程,对应进程说到这些信息后最终传递给BroadcastReceiver对象的onReceive方法。
-
重复上面第2步流程,直到取完所有的BroadcastRecord为止
下面是相关代码,请自行取阅:
ini
//BroadcastQueue.java
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
省略代码······
//如果mParallelBroadcasts中有元素,则进入下面逻辑
while (mParallelBroadcasts.size() > 0) {
//获取第一个BroadcastRecord
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
省略代码·····
final int N = r.receivers.size();
//遍历所有的receivers
for (int i=0; i < N; i++) {
//从receivers中获取receiver
Object target = r.receivers.get(i);
//把广播分发给receiver
deliverToRegisteredReceiverLocked(r,
(BroadcastFilter) target, false, i);
}
//加入历史记录
addBroadcastToHistoryLocked(r);
}
省略代码······
}
分发有序广播
如上图有序广播要求只有前一个广播接收者处理完毕 ,才能把广播分发给下一个广播接收者 (如果存在下个广播接收者的话),正因为这个原因,因此分发有序广播 的过程可是要比分发无序广播的过程复杂多了。 那就来看下复杂都体现在哪些方面。
取BroadcastRecord
在分发无序广播时,从无序广播队列 中把索引0处的BroadcastRecord取出来即可 (前提是无序广播队列不为空),而分发有序广播时,可不是简简单单的从有序广播队列中取出BroadcastRecord就可以了,请先看下面代码:
ini
//BroadcastDispatcher
public BroadcastRecord getNextBroadcastLocked(final long now) {
if (mCurrentBroadcast != null) {//当前的BroadcastRecord不为null,则直接返回
return mCurrentBroadcast;
}
final boolean someQueued = !mOrderedBroadcasts.isEmpty();//有序广播不为空
BroadcastRecord next = null;
省略代码······
//没有取出上面这种类型广播,若alarm类型不为空,则取出alarm类型
if (next == null && !mAlarmBroadcasts.isEmpty()) {
next = popLocked(mAlarmBroadcasts);
if (DEBUG_BROADCAST_DEFERRAL && next != null) {
Slog.i(TAG, "Next broadcast from alarm targets: " + next);
}
}
//若还是没有取出上面这种类型广播,并且mDeferredBroadcasts不为空,则尝试从mDeferredBroadcasts中取
if (next == null && !mDeferredBroadcasts.isEmpty()) {
省略代码······
}
//若还是没有取出,并且有序广播不为空,则取有序广播
if (next == null && someQueued) {
next = mOrderedBroadcasts.remove(0);
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG, "Next broadcast from main queue: " + next);
}
}
//赋值给mCurrentBroadcast
mCurrentBroadcast = next;
return next;
}
如上代码,分发有序广播时都需要调用上面的方法获取BroadcastRecord,取BroadcastRecord的顺序如下:
- 先判断mCurrentBroadcast是否为null,不为null则说明当前正在处理的BroadcastRecord还没处理完,并且把它返回
- 从alarm类型中取BroadcastRecord,取到赋值给mCurrentBroadcast,并且返回
- 从延迟队列mDeferredBroadcasts中取,取到赋值给mCurrentBroadcast,并且返回 (关于延迟队列会在下面介绍)
- 如果有序广播队列不为空,则从索引0处取出BroadcastRecord,取到赋值给mCurrentBroadcast,并且返回
分发
在一轮分发无序广播时,有就把所有的无序广播都分发,无则不做任何处理。而在一轮分发有序广播时 可就复杂了,分发有序广播会处于分发广播给动态广播接收者 、分发广播给静态广播接收者这两者之一,那就介绍下这两者。
分发广播给动态广播接收者
此过程与无序广播时的过程基本是一致的,除了会启动超时 和延迟机制外。
分发广播给静态广播接收者
因为有序广播的接收者可以是静态广播接收者 ,分发广播给静态广播接收者的过程可是要麻烦的很多,如果静态广播接收者的对应进程已经启动了,则把广播分发给即可;如果进程没有启动,则需要等待该进程启动,等进程启动完成再把广播分发给它。同样也会启动超时 和延迟机制。
如下是相关代码:
java
//BroadcastQueue.java
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
省略代码······
//等待 过程
if (mPendingBroadcast != null) {
boolean isDead;
//若进程还依然活着,则继续等待
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
//进程已经死掉了,不会需要处理该BroadcastRecord,mPendingBroadcast置为null
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
//取BroadcastRecord
do {
final long now = SystemClock.uptimeMillis();
// 获取下一个BroadcastRecord
r = mDispatcher.getNextBroadcastLocked(now);
省略代码······
}while (r == null);
省略代码······
//取接收者
final Object nextReceiver = r.receivers.get(recIdx);
//接收者为动态广播接收者,则分发广播给接收者
if (nextReceiver instanceof BroadcastFilter) {
省略代码······
}
//把广播分发给静态广播接收者
ResolveInfo info = (ResolveInfo)nextReceiver;
省略代码······
}
超时机制
正因为有序广播要求只有前一个广播接收者处理完毕 ,才能把广播分发给下一个广播接收者 ,这样的要求会带来一个很严重的问题:如果一个接收者处理广播花费了很久的时间 ,那就会导致后面的接收者 和有序广播队列中的其他BroadcastRecord分发延迟 ,那针对此情况就有了超时机制。
超时机制在把广播分发给每一个接收者之前会启动 ,当接收者处理完毕广播后,需要发送一个处理完毕的反馈给广播分发中心,这时候取消超时机制。如果在规定的超时时间内,没有收到该接收者的反馈,则认为是超时对应的App进程就会报ANR (Application not response)。关于超时机制在后面还会详细介绍。
延迟机制
对于一些接收者处理广播的速度慢时,会把该接收者对应的uid等信息加入延迟队列,当在规定的延迟时间内,再次给该接受者发送广播时,延迟它的分发时间。可以理解为是对该接受者的惩罚措施吧。关于延迟机制在后面还会介绍,下面是延迟队列的相关代码:
swift
//BroadcastDispatcher.java
//延迟队列
private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
5.2.3 分发广播模块分类
尤其是有序广播的分发会由于某个接收者处理耗时导致其他的接收者以及广播分发延迟的情况 ,针对此情况对分发广播模块进行了分类 做到"专车专用"的作用,也就是特定的分发广播模块只处理特定的广播的分发,而分发广播模块被划分为前台广播分发模块 、后台广播分发模块 、offload分发广播模块。
前台广播分发模块
前台广播分发模块只处理前台广播的分发,在上面介绍广播可以按是否是后台划分为前台广播 和后台广播。
后台广播分发模块
后台广播分发模块则只处理后台广播的分发,广播发送者在默认发送广播的情况下发送的就是后台广播。
offload分发广播模块
offload分发广播模块主要处理boot complete广播的分发,也就是系统启动时候的广播的分发都由它处理,而App进程是不能使用它的。
如下是相关代码:
ini
//ActivityManagerService
public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
省略代码·····
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", backConstants, true);
mBgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
"offload_bg", offloadConstants, true);
mFgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
"offload_fg", foreConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
mBroadcastQueues[2] = mBgOffloadBroadcastQueue;
mBroadcastQueues[3] = mFgOffloadBroadcastQueue;
省略代码······
}
5.3 小结
广播分发中心 分为注册模块 和分发广播模块。
注册模块 所做的事情就是提供动态BroadcastReceiver注册的能力,以及保存注册信息 ,保存下来的注册信息供发送广播时使用。
分发广播模块 所做的事情就是一轮一轮的把广播发送给接收者 ,把广播发送给接收者又分为发送所有无序广播 和发送有序广播 ,发送所有无序广播就是如果无序广播队列 不为空,把所有的无序广播发送出去。而发送有序广播时,因为有序广播要求只有前一个广播接收者处理完毕 ,才能把广播分发给下一个广播接收者 ,也正因为有如此要求会导致一个问题:如果一个接收者处理广播花费了很久的时间 ,那就会导致后面的接收者 和有序广播队列中的其他BroadcastRecord分发延迟 ,为了解决此问题设计了超时机制 ,延迟机制 ,对分发广播模块进行分类,做到专模块负责专广播的作用。
题外话:即使有了上面的这些机制,在实际项目中我也遇到过由于某个接收者的耗时,导致其他接收者及广播被延迟几十毫秒甚至一分钟的情况出现。
6. 广播发送接收流程
在一文彻底搞懂Android广播的所有知识 (上)文中分别从广播发送者 、广播接收者 、广播分发中心 三方面介绍了广播机制 ,而广播发送接收流程就是从一个整体的角度来把它们串联起来,看它们之间是如何配合来保证广播的发送和接收的。
同样我也绘制了一幅图如下:
图解
上图展示了广播发送和接收的流程,其实整个流程的图要比上面的复杂多了,我一贯遵循先简后难 的原则,该流程大致分为发送广播 、收集、入队 、分发 、接收广播这五个步骤,那就从这五个步骤来介绍下广播是如何发送的?广播又是如何被接受的?
6.1 发送广播
在一文彻底搞懂Android广播的所有知识 (上)文中介绍过广播按是否有序 分为有序广播 和无序广播 ;广播按是否是后台 分为后台广播 和前台广播 ;当然以前广播还有sticky广播粘性广播,但是该广播由于安全原因已经被废弃了,故不再赘述。
而广播发送者调用Context的sendBroadcastXXX 和sendOrderedBroadcastXXX 方法时可以发送无序广播和有序广播。在发送广播时为Intent对象增加Intent.FLAG_RECEIVER_FOREGROUND属性即可发送前台广播,不加则发送后台广播。
发送广播调用的是ActivityManager的broadcastIntentWithFeature方法,而该方法是一个binder调用,最终会调用到AMS的broadcastIntentWithFeature方法。也就是广播发送者就是一个"甩手掌柜",它只需要把发送的广播数据 (广播数据存放于Intent对象中) 通过binder通信传递给广播分发中心 ,那接下来的"重任"就落在了广播分发中心"身上"了。
6.2 收集、入队
收集、入队 很明显这是两个过程,先有收集 ,后才有入队 ,那就先从收集讲起。
6.2.1 收集
大家可以想一想,一些比较复杂的观察者模式 例子,比如鼎鼎有名的EventBus,发送者发送了一个event事件后,要做的第一件事就是把观察该event的观察者收集起来,其次在把event事件分发给这些观察者们。
同样广播分发中心 在收到一个广播数据后,要做的第一件事情就是收集 ,收集就是把观察该广播的所有接收者 (也可以称为注册信息) 收集起来。
收集receiver的过程分为收集静态接收者 和收集动态接收者。
收集静态接收者
静态接收者指的是在AndroidManifest文件中声明的BroadcastReceiver,而收集静态接收者就是从PMS根据Intent对象中的action等信息把所有的BroadcastReceiver都收集起来,当然收集静态接收者有个前提就是发送者没有指定只把广播发送给动态接收者 。每一个静态接收者信息是存放在ResolveInfo对象中的,如下是它的相关属性:
属性 | 说明 |
---|---|
activityInfo:ActivityInfo | activityInfo就代表的是配置在AndroidManifest中的BroadcastReceiver信息 |
filter:IntentFilter | filter对应了在AndroidManifest中配置的BroadcastReceiver的intent-filter信息,比如action |
如下是相关的代码:
less
//ActivityManagerService
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList) {
省略代码······
List receivers = null;
//没有指定只有动态广播接收者接收广播
if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
//从pms中收集所有的接受者,接收者是一个ResolveInfo列表
receivers = collectReceiverComponents(
intent, resolvedType, callingUid, users, broadcastAllowList);
}
省略代码······
}
收集动态接收者
动态接收者即所谓的通过代码的方式注册的广播接收者,因为在注册模块 中存储了所有的注册信息,因此收集动态接收者就是从注册模块中根据Intent对象的action信息把所有的接收者收集起来。在一文彻底搞懂Android广播的所有知识 (上)文中介绍过注册模块 以及注册信息的存放。每个动态接收者信息是存放在BroadcastFilter对象中的,那我再带大家来回顾下BroadcastFilter类的主要属性:
BroadcastFilter
还是看上面类图,BroadcastFilter继承了IntentFilter类,该类存储了广播相关的信息,下面罗列了它的几个关键属性:
属性 | 说明 |
---|---|
receiverList:ReceiverList | receiverList存储了类型为IIntentReceiver的receiver以及进程相关信息 |
packageName:String | 包名信息 |
而广播的action信息存储在它的父类的mActions属性中,如下:
属性 | 说明 |
---|---|
mActions:ArrayList | mActions存储了所有的action |
也就是说一个BroadcastFilter对象是可以包含多个广播的 ,而通过BroadcastFilter对象的receiverList属性找到ReceiverList对象。那再来看下ReceiverList类。
ReceiverList
该类的主要作用就是把注册的信息都存储起来,下面列出了该类的几个关键属性:
属性 | 说明 |
---|---|
receiver:IIntentReceiver | receiver可以理解为是BroadcastReceiver的"代理人",receiver如果是App进程传递过来它的类型是BinderProxy;如果是systemserver进程传递过来它的类型是Binder |
app:ProcessRecord | app对应App进程的进程信息,如进程id、进程状态等 |
pid:int | pid代表App进程的进程id |
uid:int | uid对应App的唯一id |
小结下BroadcastFilter
一个BroadcastFilter对象可以包含多个action ,一个action对应一个广播 ,可以通过BroadcastFilter对象的receiverList属性 ,找到远端BroadcastReceiver对象的 "代理人"receiver ,它的类型为IIntentReceiver是一个匿名binder服务,请记住这个**"代理人"receiver可是非常重要**。
如下是收集动态接收者的相关代码:
less
//ActivityManagerService
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList) {
省略代码······
List<BroadcastFilter> registeredReceivers = null;
if (intent.getComponent() == null) {
final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
//不走着
if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
省略代码······
} else {
//从mReceiverResolver对象的queryIntent方法中根据intent中的action,查找所有的BroadcastFilter
registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
resolvedType, false /*defaultOnly*/, userId);
}
}
小结
收集阶段会把观察该广播的所有动态接收者 和所有静态接收者 全部收集起来,每一个动态接收者是存放于BroadcastFilter对象 ,每一个静态接收者是存放于ResolveInfo对象 ,所有的动态接收者存放于类型为List的registeredReceivers 变量中,所有静态接收者存放于类型为List的receivers变量中。
既然广播相关的接收者都已经收集起来了,那接下来介绍下入队。
6.2.2 入队
为啥要"入队"呢?其实广播分发中心 就是一个消费者/生产者模式,因为需要分发的广播非常多,因此需要被分发的广播是需要放入队列中的,而在分发广播时,再从队列中取出广播进行分发。因此入队就是把广播放入队列中,那这里提到的广播到底是啥呢?
这里提到的广播就是BroadcastRecord对象,BroadcastRecord对象来广播信息和接收者等信息封装起来 (在一文彻底搞懂Android广播的所有知识 (上)文中介绍过BroadcastRecord),进而把BroadcastRecord对象放入队列中。那这里的队列到底是啥呢?在揭开谜底之前还是先来回顾下BroadcastRecord类吧。
队列元素
BroadcastRecord作为队列的元素,这个类的作用主要是用来记录一个广播及其他信息,先看下该类的主要属性:
属性 | 说明 |
---|---|
intent:Intent | 广播发送者发送广播时的intent信息,广播信息以及参数都在intent对象中 |
callerApp:ProcessRecord | 广播发送者的进程信息,比如pid、进程状态等 |
callerPackage:String | 广播发送者的包名 |
callingPid:int | 广播发送者的进程id |
callerPackage:String | 广播发送者的包名 |
callingPid:int | 广播发送者的进程id |
ordered:boolean | 广播是否是有序广播 |
sticky:boolean | 广播是否是粘性广播 |
receivers:List | 广播的接收者信息,接收者既包含动态广播接收者 也包含静态广播接收者 |
delivery:int[] | 对应receivers,每个广播接收者的状态 |
duration:long[] | 对应receivers,每个广播接收者从接收到处理完广播所花的时间 |
deferred:boolean | 该BroadcastRecord是否被延迟 |
enqueueTime:long | 该BroadcastRecord对象入队列的时间,所有的BroadcastRecord是需要放入队列中,才能被发送 |
dispatchTime:long | 开始分发该广播的时间 |
timeoutExempt:boolean | 是否豁免超时机制 |
nextReceiver:int | 下一个广播接收者的索引值,因为所有的广播接收者都是放在List中的 |
anrCount:int | 发生anr的次数 |
curFilter:BroadcastFilter | 当前的动态广播接收者 |
curApp:ProcessRecord | 当前广播接收者对应的进程信息 |
其中receivers属性是List类型的,该属性存放了该广播的所有接收者,请注意该属性在后面会用到它。回顾完BroadcastRecord来看下都有哪些队列吧。
都有哪些队列
在一文彻底搞懂Android广播的所有知识 (上)文中介绍过广播分发中心 中有有序广播队列 和无序广播队列 两种类型队列,这只是按队列是否存放有序还是无序广播划分的,还有另外一种划法按分发广播模块种类划分 。同样在一文彻底搞懂Android广播的所有知识 (上)文中介绍过因为有序广播的分发会出现延迟 ,导致后面的广播分发也会被延迟 ,因此对分发广播模块进行划分 ,作为专模块专用的目的 ,分发广播模划分为offload分发广播模块 、后台广播分发模块 、前台广播分发模块 ,而每个分发模块都有自己的有序广播队列 和无序广播队列。
我同样绘制了一幅图:
因此在入队之前先要选择是哪个分发广播模块 ,默认情况下是选用后台分发广播模块 ,如果发送者发送的是前台广播,则会选用前台分发广播模块。至于offload分发模块不是为普通App使用的,故不赘述。
下面是相关代码:
kotlin
//ActivityManagerService
BroadcastQueue broadcastQueueForIntent(Intent intent) {
if (isOnFgOffloadQueue(intent.getFlags())) {
return mFgOffloadBroadcastQueue;
}
if (isOnBgOffloadQueue(intent.getFlags())) {
return mBgOffloadBroadcastQueue;
}
//intent中的flags包含FLAG_RECEIVER_FOREGROUND则认为是 前台分发广播模块
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
//isFg为true,则是 前台广播模块 否则是 后台广播模块
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
分发广播模块选定了,接下来该看到底是入队到有序广播队列 还是无序广播队列 ,那我就从有序广播 和无序广播分别来介绍下吧。
有序广播
对于发送者发送的是有序广播,不管广播的接收者是动态的还是静态的,构造的BroadcastRecord对象都是入队到有序广播队列 ,入队到有序广播的BroadcastRecord对象可是有一个特别要注意的地方:BroadcastRecord的receivers属性中存放的所有接收者是有优先级一说的。
这也很好理解,因为有序广播这里的有序指的是接收者是按一个一个的顺序来接收广播的,凡是有了顺序那就会存在特权或者优先级一说,比如在现实生活中排队购买火车票,军人和老人的优先级要高于普通人,它们可以优先购买。而对于广播的接收者来说也存在优先级一说,谁的优先级最高谁就会最先收到广播。
首先接收者的优先级是可以配置 的,对于静态接收者的优先级是在AndroidManifest中配置的,如下代码:
ini
//AndroidManifest.xml
<receiver android:name=".MyReceiver" android:exported="true">
## 通过priority 把优先级设置为100
<intent-filter android:priority="100">
<action android:name="com.niu.broadcast.demo"/>
</intent-filter>
</receiver>
对于动态接收者的优先级是在注册时进行设置的,如下代码:
ini
IntentFilter intentFilter = new IntentFilter("com.niu.broadcast.demo");
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
//设置接收者的优先级
intentFilter.setPriority(100);
registerReceiver(br,intentFilter);
如果动态接收者优先级与静态接收者优先级相同,那该如何排序呢?
如果相同,则动态接收者要排于静态接收者之前 ,这也很好理解,动态接收者对应的App进程肯定是活着的,而静态接收者对应的进程不一定活着,没活着就会存在启动对应进程的过程,因此遵循活着的优先原则。
基于以上原则可以得出如下图:
如上图,BroadcastRecord对象中的receivers,接收者优先级最高,该接收者排在最前面,如果动态接收者优先级与静态接收者优先级相同,则动态接收者要排于静态接收者之前。
题外话:对于有序广播,如果想要让接收者优先接收到广播,则把接收者的优先级提高。
下面是有关优先级排序的代码:
ini
//ActivityManagerService
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList) {
省略代码······
if (receivers != null) {
省略代码······
int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
//合并ResolveInfo和BroadcastFilter,
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
省略代码·····
}
无序广播
对于发送者发送的无序广播,需要根据动态接收者和静态接收者,来分别构造不同的BroadcastRecord对象,存放于不同的队列。啥无序广播尽然这么复杂吗?
是的,对于所有的动态接收者,需要构建BroadcastRecord对象,并且把它放入无序广播队列 ,而对于所有的静态接收者,需要构建BroadcastRecord对象,并且把它放入有序广播队列 。这样做的目的还是因为无序广播对于接收者来说是无序的 ,并且要求所有的接收者的进程都必须活着。因此基于此就有了上面的结果。
下面是无序广播入队的相关代码:
less
//ActivityManagerService
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions,
String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int realCallingUid, int realCallingPid, int userId,
boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList) {
省略代码······
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;//niu registeredReceivers动态注册的接收器
if (!ordered && NR > 0) {//niu 非有序广播,并且存在动态广播接收器
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
if (isCallerSystem) {
checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
isProtectedBroadcast, registeredReceivers);
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered,
sticky, false, userId, allowBackgroundActivityStarts,
backgroundActivityStartsToken, timeoutExempt);
省略代码······
if (!replaced) {
//把r放入无序广播队列
queue.enqueueParallelBroadcastLocked(r);
//通知发送广播
queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
省略代码······
}
6.2.3 小结
收集、入队 分为收集 和入队两个步骤。
收集 就是根据广播的action把所有的静态接收者和动态接收者都收集起来。入队 就是创建BroadcastRecord对象把广播信息及接收者等信息封装起来,放入队列中**,而具体是放入哪个分发广播模块的哪个队列中,完全与发送者发送的广播是前台广播还是后台广播,或者是有序广播还是无序广播有关。
既然BroadcastRecord对象被放入了队列,可不要天真的认为BroadcastRecord对象立马就会被取出来进行分发的,因为在队列中还有很多的BroadcastRecord对象需要被分发,那就只能默默的等待了。
6.3 分发广播
我绘制了一幅分发广播的简单图:
图解
如上图,每一轮都是先进行发送所有无序广播 ,在进行发送有序广播 ,接着在继续下一轮,就这样循环下去。因为发送所有无序广播 已经在一文彻底搞懂Android广播的所有知识 (上)文中详细的介绍过了,在这就不在赘述了。再次详细的介绍下发送有序广播。
6.3.1 发送有序广播
发送有序广播 可是比发送所有无序广播 复杂的多多了,发送所有无序广播就是把无序广播队列中的所有广播发送出去即可,并且所有的广播接收者都是动态接收者,也就是它们所在的进程都活着。而发送有序广播多么的复杂,请看下图便知:
图解
如上图展示了一轮发送有序广播的流程,该轮结束后接着进入下一轮,就这样依次不断地循环下去。那就结合上图来详细的介绍下发送有序广播吧。
注:以下流程涉及的相关代码都是在BroadcastQueue的processNextBroadcastLocked方法
1. 是否有挂起的广播
先说下为啥有此过程吧,在有序广播的接收者中会存在静态接收者,而静态接收者会存在一种情况就是接收者对应的App进程是没启动的 ,针对此情况就需要先把接收者对应的App进程启动,而这个启动过程是比较耗时的,即使耗时也必须等待 ,等待该App进程对应的接收者处理了该广播 ,在此等待期间,是不能分发其他的有序广播的。而处于以上状态的广播 (BroadcastRecord) 就处于挂起状态。
下面是相关代码:
ini
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//挂起的BroadcastRecord对象会赋值给mPendingBroadcast
if (mPendingBroadcast != null) {
boolean isDead;
if (mPendingBroadcast.curApp.getPid() > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.getPid());
isDead = proc == null || proc.mErrorState.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.isPendingStart();
}
//对应的app进程没有死掉,返回等待接收者反馈结果
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
//对应的app进程死掉了,则忽略该接受者,把广播发送给下个接收者
mPendingBroadcast.state = BroadcastRecord.IDLE;
//下个接收者
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
如上代码,如果存在挂起广播并且对应进程没有死掉,则只能继续等待 ,有序广播队列中的所有广播都处于等待状态。但是无序广播还是可以正常发送的。
2. 获取有序BroadcastRecord
在一文彻底搞懂Android广播的所有知识 (上)文中介绍过需要调用BroadcastDispatcher对象的getNextBroadcastLocked方法获取有序BroadcastRecord对象,BroadcastDispatcher对象其中包含了有序广播队列 。关于更具体的获取过程,请看此文
2.1 是否为null
就是判断获取的BroadcastRecord对象是否为null,为null表明有序广播队列中没有任何BroadcastRecord,直接结束本轮即可;否则进入下一步骤。
2.2 状态是否不为IDLE
每一个BroadcastRecord对象都有一个state 属性,它表明了当前BroadcastRecord对象所处的状态,对应的状态有IDLE、APP_RECEIVE、CALL_IN_RECEIVE、CALL_DONE_RECEIVE等,当state属性不为IDLE时,表明当前BroadcastRecord对象正在处于忙碌中,比如等待接收者发送反馈消息,这时候结束本轮 ,有序广播队列中的所有广播都处于等待状态;否则进入下一步骤。
相应代码如下:
kotlin
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//广播的状态不等于IDLE,则返回,比如该r正在等待一BroadcastReceiver处理完毕的返回
if (r.state != BroadcastRecord.IDLE) {
return;
}
2.3 是否分发完毕
是否分发完毕是指当前的BroadcastRecord对象的所有接收者是否都接收完词广播了,是的话就需要做相应的处理,比如把该BroadcastRecord对象放入历史记录中,取消超时机制,把该BroadcastRecord对象从有序广播队列中移除,再次获取下一个有序BroadcastRecord;否则进入下一步骤。
2.4 是否是第一次分发
是否是第一次分发是指当前的BroadcastRecord对象是不是第一次分发,是的话就需要设置该BroadcastRecord对象的一些属性,并且进入下一步;否则也需要进入下一步。
下面是相关代码:
ini
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//recIdx为0代表是第一次分发该广播
if (recIdx == 0) {
//把BroadcastRecord对象的各种dispatch属性,也就是分发时间 记录下来
r.dispatchTime = r.receiverTime;
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
省略其他代码······
}
3. 尝试启动超时机制
启动超时机制前面为啥要加尝试俩字呢,其原因是如果已经启动了超时机制,则没必要启动了,超时机制就是为了避免接收者处理广播的时间过长而设置的,对于后台广播的超时时间是60s,前台广播的超时时间是10s。超时机制实现原理非常简单就是在超时时间后进行超时处理。
如下是相关代码:
java
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//如果mPendingBroadcastTimeoutMessage为false,则启动超时机制
if (! mPendingBroadcastTimeoutMessage) {
//超时时间,mConstants.TIMEOUT后台广播为60s,前台为10s
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
//调用该方法启动超时机制
setBroadcastTimeoutLocked(timeoutTime);
}
//setBroadcastTimeoutLocked方法
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
//创建一个what为BROADCAST_TIMEOUT_MSG的Message
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
//在timeouttime后发送上面的Message
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
4. 取出广播接收者
超时机制启动后,就可以把BroadcastRecord对象的receivers 属性中的接收者取出来了,receivers属性是一个List队列,取时是从0索引位置开始往后取。
如下是相关代码:
dart
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//recIdx代表索引值
final Object nextReceiver = r.receivers.get(recIdx);
如上代码取出的nextReceiver既可以是动态接收者,也可以是静态接收者。
5. 分发广播给动态接收者
如果接收者为动态,则分发广播给动态接收者。还记得在上面收集动态接收者 时,一个动态接收者对应一个BroadcastFilter对象。在分发之前要做的第一件事情是验证,比如被发送的广播是需要权限的,则需要验证接收者的权限与要求的权限是否一致,不一致则不能分发,否则继续分发。
还记得可以通过BroadcastFilter对象的receiverList属性 ,找到远端BroadcastReceiver对象的 "代理人"receiver ,它的类型为IIntentReceiver是一个匿名binder服务。这时候类型为IIntentReceiver的receiver对象可就非常有用的。
在分发广播时会调用IApplicationThread的scheduleRegisteredReceiver方法,该方法是一个binder调用,它的主要参数如下:
参数 | 说明 |
---|---|
receiver:IIntentReceiver | 在注册动态接收者时会传递它,是远端BroadcastReceiver对象的"代理人" |
intent:Intent | 广播的action以及参数都存放于此 |
ordered:boolean | 是否是有序广播 |
sticky:boolean | 是否是粘性广播 |
其中receiver被传递到App进程,App进程通过它可以找到最终的BroadcastReceiver对象;intent存放了广播的action以及其他参数,BroadcastReceiver对象的onReceive方法会被调用,其中的参数就是该intent。
在把广播数据发送给接收者后,还需要把当前的BroadcastRecord对象的state 属性置为CALL_DONE_RECEIVE 状态,代表正在等待接收者的反馈。因为这时候有超时机制 ,如果接收者在超时时间内没有给反馈信息 ,则认为已经超时 ,也即该接收者对应的App进程发生ANR。
下面是相关代码:
scss
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//是动态接收者则进入下面流程
if (nextReceiver instanceof BroadcastFilter) {
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
//分发广播
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
省略其他代码······
return;
}
6. 分发广播给静态接收者
如果接收者为静态,则分发广播给静态接收者。还记得在上面收集静态接收者 时,一个静态接收者对应一个ResolveInfo对象。在分发之前同样要做一系列的验证,验证通过则继续分发;否则停止分发。
广播分发给静态接收者的过程可是比动态接收者麻烦很多,因为静态接收者会存在对应的App进程没有启动的情况,针对此情况还需要等待App进程启动;如果App进程启动了则无此过程。
因为静态接收者是像动态接收者有一个注册的过程,因此不存在对应的IIntentReceiver类型的"代理人"。分发广播给静态接收者是调用IApplicationThread的scheduleReceiver方法,该方法同样也是一个binder调用,它的主要参数如下:
参数 | 说明 |
---|---|
info:ActivityInfo | 在AndroidManifest文件中配置的BroadcastReceiver信息存放于此 |
intent:Intent | 广播的action以及参数都存放于此 |
其中info的作用与启动Activity时一样,App进程在接收到它后,会初始化对应的BroadcastReceiver对象。
在把广播数据发送给接收者后,还需要把当前的BroadcastRecord对象的state 属性置为APP_RECEIVE 状态,因为这时候有超时机制 ,如果接收者在超时时间内没有给反馈信息 ,则认为已经超时 ,也即该接收者对应的App进程发生ANR。
如下是相关代码:
scss
//以下代码位于BroadcastQueue的processNextBroadcastLocked方法
//静态接收者对应的进程已经启动了
if (app != null && app.getThread() != null && !app.isKilled()) {
try {
省略代码······
//分发广播
processCurBroadcastLocked(r, app,
BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when sending broadcast to "
+ r.curComponent, e);
} catch (RuntimeException e) {
省略代码······
//接收者在处理广播时抛异常,则把当前广播标记为完成
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
//分发下一个广播给接收者
scheduleBroadcastsLocked();
//必须把状态标记为IDLE
r.state = BroadcastRecord.IDLE;
return;
}
}
//静态接收者的进程没有启动,则调用AMS的startProcessLocked方法开始启动进程
r.curApp = mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
r.intent.getAction()),
isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
//进程没有启动成功,则接着下发下一个广播
if (r.curApp == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
+ receiverUid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
//把该广播信息挂起,当App进程启动后会根据mPendingBroadcast判断是否需要接收该广播
mPendingBroadcast = r;
//记录接收者的索引值
mPendingBroadcastRecvIndex = recIdx;
6.3.2 小结
分发广播会分为N多轮,每一轮都是先发送所有无序广播 ,在发送有序广播 ,发送有序广播可是比发送无序广播复杂多了,发送无序广播只需要把广播发送给所有接收者即可,也不需要等待它们的反馈,并且也没有超时机制。
而发送有序广播的接收者会存在动态接收者和静态接收者,因为是有序的所以需要接收者处理完毕广播后发送反馈信息,再收到反馈信息后才能接着把广播发送给下一个接收者。如果一个接收者处理广播的时间很长,那这种情况就会导致后面的广播延迟发送,针对此情况设计了超时机制,如果接收者在规定的超时时间内没有发送反馈信息,则认为它已经超时,则该接收者对应的进程会报ANR。
因为发送有序广播的接收者存在静态和动态之分,对于静态接收者和动态接收者也分别使用不同的接口把广播数据发送给它们。那就来看下接收者如何接收广播的。
6.4 接收广播
App进程的BroadcastReceiver对象接收广播同样也分为动态接收者接收无序广播 、动态接收者接收有序广播 、静态接收者接收有序广播,那就依次来介绍下它们吧。
6.4.1 动态接收者接收无序广播
无序广播发送给动态接收者是通过IApplicationThread的scheduleRegisteredReceiver 方法,最终会调用ApplicationThread对象的scheduleRegisteredReceiver方法,其中该方法比较重要的参数如下:
参数 | 说明 |
---|---|
receiver:IIntentReceiver | 在注册动态接收者时会传递它,是远端BroadcastReceiver对象的"代理人" |
intent:Intent | 广播的action以及参数都存放于此 |
ordered:boolean | 是否是有序广播 |
sticky:boolean | 是否是粘性广播 |
App进程收到的类型为IIntentReceiver的receiver参数已经被binder驱动底层转换为原先注册时的Binder对象,而通过receiver最终可以找到BroadcastReceiver对象,并且把intent对象传递给BroadcastReceiver对象的onReceive方法,因为是无序广播因此不需要给AMS发送反馈消息。
如下是相关代码:
scala
//LoadedApk.java
final class Args extends BroadcastReceiver.PendingResult {
public final Runnable getRunnable() {
return () -> {
//mReceiver为注册时的BroadcastReceiver对象
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
省略代码······
try {
省略代码······
receiver.setPendingResult(this);
//调用BroadcastReceiver的onReceive方法,把Context和intent传递给它
receiver.onReceive(mContext, intent);
} catch (Exception e) {
//注册并且时有序广播,需要发送反馈信息给AMS
if (mRegistered && ordered) {
//发送反馈信息
sendFinished(mgr);
}
省略代码······
}
if (receiver.getPendingResult() != null) {
//正常处理结束,会发送反馈信息给AMS
finish();
}
}
6.4.2 动态接收者接收有序广播
动态接收者接收有序广播与接收无序广播的流程基本一致,除了调用ActivityManager的finishReceiver 方法发送反馈信息外,对于有序广播来说必须发送反馈信息,这一切都已经在LoadedApk 类中做了处理,开发者完全对于这一切无感,只需要在BroadcastReceiver对象的onReceive方法中把自己的逻辑处理好即可。但是要千万注意onReceive方法中觉得不能有耗时的操作,有耗时操作会影响其他接收者接收广播,并且有可能会报ANR。
6.4.3 静态接收者接收有序广播
对于静态接收者来说不管发送者发送的是有序广播还是会无序广播,它都是当有序广播来接收,接收有序广播就需要有一个必须条件接收者处理完毕需要调用ActivityManager的finishReceiver方法发送反馈信息给AMS。
静态接收者是通过调用ApplicationThread对象的scheduleReceiver方法来接收广播的,该方法的主要参数如下:
参数 | 说明 |
---|---|
info:ActivityInfo | 在AndroidManifest文件中配置的BroadcastReceiver信息存放于此 |
intent:Intent | 广播的action以及参数都存放于此 |
静态接收者因为没有注册的过程,因此需要使用类型为ActivityInfo的info参数来构建BroadcastReceiver对象,别看到ActivityInfo是与Activity有关的,其实它与BroadcastReceiver也是有关系的,info存储了在AndroidManifest文件中声明的BroadcastReceiver信息。因此可以根据它来创建BroadcastReceiver对象,进而调用BroadcastReceiver对象的onReceive方法,把intent参数传递给它。
如下是相关代码:
scss
//ActivityThread
private void handleReceiver(ReceiverData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManager.getService();
Application app;
BroadcastReceiver receiver;
ContextImpl context;
try {
//获取Application对象
app = packageInfo.makeApplicationInner(false, mInstrumentation);
//获取context对象
context = (ContextImpl) app.getBaseContext();
省略代码······
//获取CLassLoader对象
java.lang.ClassLoader cl = context.getClassLoader();
省略代码······
//创建receiver实例
receiver = packageInfo.getAppFactory()
.instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
省略代码······
}
try {
省略代码······
receiver.setPendingResult(data);
//调用receiver的onReceive方法
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
省略代码······
} finally {
sCurrentBroadcastIntent.set(null);
}
//不为null
if (receiver.getPendingResult() != null) {
//发送反馈消息给AMS
data.finish();
}
}
6.4.4 小结
不管是动态接收者接收有序广播 还是静态接收者接收有序广播 都需要调用ActivityManager的finishReceiver 方法发送反馈信息即处理完毕信息 给AMS,AMS会把该信息转交给广播分发中心 ,进而广播分发中心 把超时机制取消了,并且接着继续分发下一个广播。而动态接收者接收无序广播 是不需要发送处理完毕消息给AMS的。
6.5 小结
本节从发送广播 、收集、入队 、分发广播 、接收广播 四步介绍了广播发送和接收流程。广播的发送和接收流程其实就是一个生产者/消费者模式,发送接收过程如下:
-
广播发送者可以发送有序广播/无序广播/后台广播/前台广播
-
而广播分发中心 在收到广播后会进行收集、入队 的操作,收集就是把观察该广播的动态接收者和静态接收者都收集起来,入队就是使用BroadcastRecord对象把广播和所有的接收者以及其他信息都封装起来,并且放入队列中,而这里的队列到底是哪个广播分发模块的有序广播队列还是无序广播队列都需要根据广播有序广播还是无序广播,是前台广播还是后台广播,是否存在静态接收者等信息来决定
-
不管是哪个广播分发模块分发广播都在同一个线程内分发 ,也就是只存在一个消费者。在每一轮分发广播时都会先分发所有无序广播 ,再分发有序广播 ,有序广播的分发要比无序广播复杂的多,基于此有序广播存在超时机制,而无序广播没有超时机制。
-
App进程在收到广播后,对于有序广播需要在处理完广播后发送处理完毕消息 给AMS,AMS在把消息转交给广播分发中心 ,广播分发中心 会取消超时机制,同时 会接着分发下一个广播。对于无序广播则不需要发送处理完毕消息给AMS。
7. 总结
本文从广播机制 、广播机制原理 、广播接收者 、广播发送者 、广播分发中心 、广播发送和接收流程介绍了广播相关的所有知识:
-
广播机制:介绍了广播的使用和作用
-
广播机制原理:介绍了广播机制的基本原理是啥,都由哪些部分构成
-
广播接收者:介绍了广播的接收者分为动态接收者和静态接收者,动态接收者的注册过程
-
广播发送者:介绍了广播发送者发送的广播的类别都有哪些
-
广播分发中心:分别从注册模块和分发广播模块介绍了动态广播的注册,及广播的分发
-
广播发送和接收流程:从一个更全面的角度介绍了广播发送者、广播接收者、广播分发中心之间是如何配合来保证广播机制的完美工作的