您的位置:首页 > 其它

广播(Broadcast内部机制讲解)

2014-09-25 16:00 267 查看
http://my.oschina.net/youranhongcha/blog/226274

摘要 我们在编写Android程序时,常常会用到广播(Broadcast)机制。从易用性的角度来说,使用广播是非常简单的。不过,这个不是本文关心的重点,我们希望探索得再深入一点儿。我想,许多人也不想仅仅停留在使用广播的阶段,而是希望了解一些广播机制的内部机理。如果是这样的话,请容我斟一杯红茶,慢慢道来。
Android Broadcast Receiver

目录[-]

1 概述

2 两种receiver

2.1 动态receiver

2.2 静态receiver

3 激发广播

3.1 AMS一侧的broadcastIntentLocked()

3.1.1 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记

3.1.2 处理和package相关的广播

3.1.3 处理其他一些系统广播

3.1.4 判断当前是否有权力发出广播

3.1.5 必要时更新一下系统中的sticky广播列表

3.1.6 尝试向并行receivers递送广播

3.1.7 整理两个receiver列表

3.1.8 尝试逐个向串行receivers递送广播

3.2 最重要的processNextBroadcast()

3.2.1 用deliverToRegisteredReceiverLocked()递送到平行动态receiver

3.2.2 静态receiver的递送

3.2.2.1 processCurBroadcastLocked()

3.2.2.2 必要时启动新进程

3.2.3 说说有序广播是如何循环起来的?

3.2.4 说说有序广播的timeout处理

4 尾声

品茗论道说广播(Broadcast内部机制讲解)

侯 亮

1 概述

我们在编写Android程序时,常常会用到广播(Broadcast)机制。从易用性的角度来说,使用广播是非常简单的。不过,这个不是本文关心的重点,我们希望探索得再深入一点儿。我想,许多人也不想仅仅停留在使用广播的阶段,而是希望了解一些广播机制的内部机理。如果是这样的话,请容我斟一杯红茶,慢慢道来。

简单地说,Android广播机制的主要工作是为了实现一处发生事情,多处得到通知的效果。这种通知工作常常要牵涉跨进程通讯,所以需要由AMS(Activity Manager Service)集中管理。





在Android系统中,接收广播的组件叫作receiver,而且receiver还分为动态和静态的。动态receiver是在运行期通过调用registerReceiver()注册的,而静态receiver则是在AndroidManifest.xml中声明的。动态receiver比较简单,静态的就麻烦一些了,因为在广播递送之时,静态receiver所从属的进程可能还没有启动呢,这就需要先启动新的进程,费时费力。另一方面,有些时候用户希望广播能够按照一定顺序递送,为此,Android又搞出了ordered
broadcast的概念。

细节如此繁杂,非一言可以说清。我们先从receiver这一侧入手吧。

2 两种receiver

Android中的receiver,分为“动态receiver”和“静态receiver”。

2.1 动态receiver

动态receiver必须在运行期动态注册,其实际的注册动作由ContextImpl对象完成:

?
注册之时,用户会把一个自定义的receiver对象作为第一个参数传入。当然,用户的receiver都是继承于BroadcastReceiver的。使用过广播机制的程序员,对这个BroadcastReceiver应该都不陌生,这里就不多说了。我们需要关心的是,这个registerReceiverInternal()内部还包含了什么重要的细节。

registerReceiverInternal()代码的截选如下:

?
请大家注意那个rd对象(IIntentReceiver rd)。我们知道,在Android架构中,广播动作最终其实都是由AMS递送出来的。AMS利用binder机制,将语义传递给各个应用进程,应用进程再辗转调用到receiver的onReceive(),完成这次广播。而此处的rd对象正是承担“语义传递工作“的binder实体。

为了管理这个重要的binder实体,Android搞出了一个叫做ReceiveDispatcher的类。该类的定义截选如下:

【frameworks/base/core/java/android/app/LoadedApk.java】

?
这样看来,“动态注册的BroadcastReceiver”和“ReceiverDispatcher节点”具有一一对应的关系。示意图如下:





一个应用里可能会注册多个动态receiver,所以这种一一对应关系最好整理成表,这个表就位于LoadedApk中。前文mPackageInfo.getReceiverDispatcher()一句中的mPackageInfo就是LoadedApk对象。

在Android的架构里,应用进程里是用LoadedApk来对应一个apk的,进程里加载了多少个apk,就会有多少LoadedApk。每个LoadedApk里会有一张“关于本apk动态注册的所有receiver”的哈希表(mReceivers)。当然,在LoadedApk初创之时,这张表只是个空表。

mReceivers表的定义如下:

?
该表的key项是我们比较熟悉的Context,也就是说可以是Activity、Service或Application。而value项则是另一张“子哈希表”。这是个“表中表”的形式。言下之意就是,每个Context(比如一个activity),是可以注册多个receiver的,这个很好理解。mReceivers里的“子哈希表”的key值为BroadcastReceiver,value项为ReceiverDispatcher,示意图如下:





图:客户进程中的mReceivers表

接下来我们继续看registerReceiverInternal(),它最终调用到

?
registerReceiver()函数的filter参数指明了用户对哪些intent感兴趣。对同一个BroadcastReceiver对象来说,可以注册多个感兴趣的filter,就好像声明静态receiver时,也可以为一个receiver编写多个<intent-filter>一样。这些IntentFilter信息会汇总到AMS的mRegisteredReceivers表中。在AMS端,我们可以这样访问相应的汇总表:

?
其中的receiver参数为IIntentReceiver型,正对应着ReceiverDispatcher中那个binder实体。也就是说,每个客户端的ReceiverDispatcher,会对应AMS端的一个ReceiverList。

ReceiverList的定义截选如下:

?
ReceiverList继承于ArrayList<BroadcastFilter>,而BroadcastFilter又继承于IntentFilter,所以ReceiverList可以被理解为一个IntentFilter数组列表。

?
现在,我们可以绘制一张完整一点儿的图:





这张图只画了一个用户进程,在实际的系统里当然会有很多用户进程了,不过其关系是大致统一的,所以我们不再重复绘制。关于动态receiver的注册,我们就先说这么多。至于激发广播时,又会做什么动作,我们会在后文阐述,现在我们先接着说明和动态receiver相对的静态receiver。

2.2 静态receiver

静态receiver是指那些在AndroidManifest.xml文件中声明的receiver,它们的信息会在系统启动时,由Package Manager Service(PKMS)解析并记录下来。以后,当AMS调用PKMS的接口来查询“和intent匹配的组件”时,PKMS内部就会去查询当初记录下来的数据,并把结果返回AMS。有的同学认为静态receiver是常驻内存的,这种说法并不准确。因为常驻内存的只是静态receiver的描述性信息,并不是receiver实体本身。

在PKMS内部,会有一个针对receiver而设置的Resolver(决策器),其示意图如下:





关于PKMS的查询动作的细节,可参考PKMS的相关文档。目前我们只需知道,PKMS向外界提供了queryIntentReceivers()函数,该函数可以返回一个List<ResolveInfo>列表。

我们举个实际的例子:

?
这是AMS的systemReady()函数里的一段代码,意思是查找有多少receiver对ACTION_PRE_BOOT_COMPLETED感兴趣。

ResolveInfo的定义截选如下:

?
总之,当系统希望发出一个广播时,PKMS必须能够决策出,有多少静态receiver对这个广播感兴趣,而且这些receiver的信息分别又是什么。

关于receiver的注册动作,我们就先说这么多。下面我们来看看激发广播时的动作。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: