当前位置:   article > 正文

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

broadcast already finished

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对象完成:

  1. <code class="hljs less" style=""><span class="hljs-variable" style="">@Override</span>  
  2. public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)   
  3. {      
  4.     return registerReceiver(receiver, filter, nullnull);  
  5. }  
  6. <span class="hljs-variable" style="">@Override</span>  
  7. public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,  
  8.                                String broadcastPermission, Handler scheduler)   
  9. {     
  10.     return registerReceiverInternal(receiver, filter, broadcastPermission,  
  11.                                     scheduler, getOuterContext());  
  12. }</code>  

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

        registerReceiverInternal()代码的截选如下:

  1. <code class="hljs kotlin" style=""><span class="hljs-keyword" style="">private</span> Intent registerReceiverInternal(BroadcastReceiver receiver,  
  2.                                         IntentFilter filter, String broadcastPermission,  
  3.                                         Handler scheduler, Context context)   
  4. {  
  5.     IIntentReceiver rd = <span class="hljs-literal" style="">null</span>;      
  6.     <span class="hljs-keyword" style="">if</span> (receiver != <span class="hljs-literal" style="">null</span>)   
  7.     {          
  8.         <span class="hljs-keyword" style="">if</span> (mPackageInfo != <span class="hljs-literal" style="">null</span> && context != <span class="hljs-literal" style="">null</span>)   
  9.         {              
  10.             <span class="hljs-keyword" style="">if</span> (scheduler == <span class="hljs-literal" style="">null</span>)   
  11.             {  
  12.                 scheduler = mMainThread.getHandler();  
  13.             }              
  14.             <span class="hljs-comment" style="">// 查找和context对应的“子哈希表”里的ReceiverDispatcher,如果找不到,就重新new一个</span>  
  15.             rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,  
  16.                                                     mMainThread.getInstrumentation(), <span class="hljs-literal" style="">true</span>);  
  17.         }   
  18.         . . . . . .  
  19.     }      
  20.     <span class="hljs-keyword" style="">try</span>   
  21.     {          
  22.         <span class="hljs-keyword" style="">return</span> ActivityManagerNative.getDefault().registerReceiver(  
  23.                 mMainThread.getApplicationThread(), mBasePackageName,  
  24.                 rd, filter, broadcastPermission);  
  25.     }   
  26.     <span class="hljs-keyword" style="">catch</span> (RemoteException e)   
  27.     {          
  28.         <span class="hljs-keyword" style="">return</span> <span class="hljs-literal" style="">null</span>;  
  29.     }  
  30. }</code>  

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

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

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

  1. <code class="hljs java" style=""><span class="hljs-keyword" style="">static</span> <span class="hljs-keyword" style="">final</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">ReceiverDispatcher</span></span></span><span class="hljs-class" style="">   
  2. </span></span>{  
  3.     <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">static</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">InnerReceiver</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">extends</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">IIntentReceiver</span></span></span><span class="hljs-class" style="">.</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">Stub</span></span></span><span class="hljs-class" style=""> </span></span>{  
  4.         . . . . . .  
  5.         . . . . . .  
  6.     }  
  7.     <span class="hljs-keyword" style="">final</span> IIntentReceiver.Stub mIIntentReceiver;   <span class="hljs-comment" style="">// 请注意这个域!它就是传到外面的rd。</span>  
  8.     <span class="hljs-keyword" style="">final</span> BroadcastReceiver mReceiver;  
  9.     <span class="hljs-keyword" style="">final</span> Context mContext;  
  10.     <span class="hljs-keyword" style="">final</span> Handler mActivityThread;  
  11.     <span class="hljs-keyword" style="">final</span> Instrumentation mInstrumentation;  
  12.     <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">boolean</span> mRegistered;  
  13.     <span class="hljs-keyword" style="">final</span> IntentReceiverLeaked mLocation;  
  14.     RuntimeException mUnregisterLocation;  
  15.     <span class="hljs-keyword" style="">boolean</span> mForgotten;  
  16.     . . . . . .</code>  

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

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

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

        mReceivers表的定义如下:

  1. <code class="hljs ruby" style="">private final   
  2. HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher<span class="hljs-meta" style="">>> </span>mReceivers  
  3.     = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher<span class="hljs-meta" style="">>></span>();</code>  

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

图:客户进程中的mReceivers表

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

  1. <code class="hljs css" style=""><span class="hljs-selector-tag" style="">ActivityManagerNative</span><span class="hljs-selector-class" style="">.getDefault</span>()<span class="hljs-selector-class" style="">.registerReceiver</span>(  
  2.                     <span class="hljs-selector-tag" style="">mMainThread</span><span class="hljs-selector-class" style="">.getApplicationThread</span>(), <span class="hljs-selector-tag" style="">mBasePackageName</span>,  
  3.                     <span class="hljs-selector-tag" style="">rd</span>, <span class="hljs-selector-tag" style="">filter</span>, <span class="hljs-selector-tag" style="">broadcastPermission</span>);</code>  

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

  1. <code class="hljs nginx" style=""><span class="hljs-attribute" style="">ReceiverList</span> rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());</code>  

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

        ReceiverList的定义截选如下:

  1. <code class="hljs java" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">ReceiverList</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">extends</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">ArrayList</span></span></span><span class="hljs-class" style=""><</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">BroadcastFilter</span></span></span><span class="hljs-class" style="">>  
  2.         </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">implements</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">IBinder</span></span></span><span class="hljs-class" style="">.</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">DeathRecipient</span></span></span><span class="hljs-class" style="">   
  3. </span></span>{  
  4.     <span class="hljs-keyword" style="">final</span> ActivityManagerService owner;   
  5.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">final</span> IIntentReceiver receiver;      
  6.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">final</span> ProcessRecord app;      
  7.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">int</span> pid;      
  8.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">int</span> uid;  
  9.     BroadcastRecord curBroadcast = <span class="hljs-keyword" style="">null</span>;  
  10.     <span class="hljs-keyword" style="">boolean</span> linkedToDeath = <span class="hljs-keyword" style="">false</span>;  
  11.     String stringName;  
  12.     . . . . . .</code>  

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

  1. <code class="hljs actionscript" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">BroadcastFilter</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">extends</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">IntentFilter</span></span></span><span class="hljs-class" style=""> </span></span>{  
  2.     <span class="hljs-keyword" style="">final</span> ReceiverList receiverList;  
  3.     <span class="hljs-keyword" style="">final</span> String packageName;  
  4.     <span class="hljs-keyword" style="">final</span> String requiredPermission;  
  5.     . . . . . .</code>  

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

这张图只画了一个用户进程,在实际的系统里当然会有很多用户进程了,不过其关系是大致统一的,所以我们不再重复绘制。关于动态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>列表。

        我们举个实际的例子:

  1. <code class="hljs php" style="">Intent intent = <span class="hljs-keyword" style="">new</span> Intent(Intent.ACTION_PRE_BOOT_COMPLETED);  
  2. <span class="hljs-keyword" style="">List</span><ResolveInfo> ris = <span class="hljs-keyword" style="">null</span>;<span class="hljs-keyword" style="">try</span> {  
  3.     ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, <span class="hljs-keyword" style="">null</span>, <span class="hljs-number" style="">0</span>, <span class="hljs-number" style="">0</span>);  
  4. } <span class="hljs-keyword" style="">catch</span> (RemoteException e) {}</code>  

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

         ResolveInfo的定义截选如下:

  1. <code class="hljs java" style=""><span class="hljs-keyword" style="">public</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">ResolveInfo</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">implements</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">Parcelable</span></span></span><span class="hljs-class" style="">   
  2. </span></span>{      
  3.     <span class="hljs-keyword" style="">public</span> ActivityInfo activityInfo;      
  4.     <span class="hljs-keyword" style="">public</span> ServiceInfo serviceInfo;      
  5.     <span class="hljs-keyword" style="">public</span> IntentFilter filter;      
  6.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">int</span> priority;      
  7.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">int</span> preferredOrder;      
  8.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">int</span> match;  
  9.     . . . . . .  
  10.     . . . . . .</code>  

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

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

 

3 激发广播

        大家常见的激发广播的函数有哪些呢?从ContextImpl.java文件中,我们可以看到一系列发送广播的接口,列举如下:

  • public void sendBroadcast(Intent intent)

  • public void sendBroadcast(Intent intent, int userId)

  • public void sendBroadcast(Intent intent, String receiverPermission)

  • public void sendOrderedBroadcast(Intent intent, String receiverPermission)

  • public void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

  • public void sendStickyBroadcast(Intent intent)

  • public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

        其中sendBroadcast()是最简单的发送广播的动作。而sendOrderedBroadcast(),则是用来向系统发出有序广播(Ordered broadcast)的。这种有序广播对应的所有接收器只能按照一定的优先级顺序,依次接收intent。这些优先级一般记录在AndroidManifest.xml文件中,具体位置在<intent-filter>元素的android:priority属性中,其数值越大表示优先级越高,取值范围为-1000到1000。另外,有时候我们也可以调用IntentFilter对象的setPriority()方法来设置优先级。

        对于有序广播而言,前面的接收者可以对接收到的广播intent进行处理,并将处理结果放置到广播intent中,然后传递给下一个接收者。需要注意的是,前面的接收者有权终止广播的进一步传播。也就是说,如果广播被前面的接收者终止了,那么后面的接收器就再也无法接收到广播了。

        还有一个怪东西,叫做sticky广播,它又是什么呢?简单地说,sticky广播可以保证“在广播递送时尚未注册的receiver”,一旦日后注册进系统,就能够马上接到“错过”的sticky广播。有关它的细节,我们在后文再说。

        在发送方,我们熟悉的调用sendBroadcast()的代码片段如下:

  1. <code class="hljs actionscript" style="">mContext = getApplicationContext();   
  2. Intent intent = <span class="hljs-keyword" style="">new</span> Intent();    
  3. intent.setAction(<span class="hljs-string" style="">"com.android.xxxxx"</span>);    
  4. intent.setFlags(<span class="hljs-number" style="">1</span>);    
  5. mContext.sendBroadcast(intent);</code>  

上面的mContext的内部其实是在调用一个ContextImpl对象的同名函数,所以我们继续查看ContextImpl.java文件。

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

  1. <code class="hljs java" style=""><span class="hljs-meta" style="">@Override</span>  
  2. <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">sendBroadcast</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(Intent intent)</span></span></span><span class="hljs-function" style="">   
  3. </span></span>{  
  4.     String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());      
  5.     <span class="hljs-keyword" style="">try</span>   
  6.     {  
  7.         intent.setAllowFds(<span class="hljs-keyword" style="">false</span>);  
  8.         ActivityManagerNative.getDefault().broadcastIntent(  
  9.             mMainThread.getApplicationThread(), intent, resolvedType, <span class="hljs-keyword" style="">null</span>,  
  10.             Activity.RESULT_OK, <span class="hljs-keyword" style="">null</span>, <span class="hljs-keyword" style="">null</span>, <span class="hljs-keyword" style="">null</span>, <span class="hljs-keyword" style="">false</span>, <span class="hljs-keyword" style="">false</span>,  
  11.             Binder.getOrigCallingUser());  
  12.     } <span class="hljs-keyword" style="">catch</span> (RemoteException e) {  
  13.     }  
  14. }</code>  

简单地调用broadcastIntent()向AMS发出请求了。

 

3.1 AMS一侧的broadcastIntentLocked()

        用户进程把发送广播的语义传递到AMS之后,最终会由AMS的broadcastIntentLocked()处理。其原型如下:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">private</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">final</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">int</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">broadcastIntentLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(ProcessRecord callerApp,  
  2.                                         String callerPackage,   
  3.                                         Intent intent, String resolvedType,  
  4.                                         IIntentReceiver resultTo, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode,   
  5.                                         String resultData,  
  6.                                         Bundle map, String requiredPermission,  
  7.                                         </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> ordered, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sticky,   
  8.                                         </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> callingPid, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> callingUid,                     
  9.                                         </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> userId)</span></span></span></span></code>  

broadcastIntentLocked()需要考虑以下方面的技术细节。

        首先,有些广播intent只能由具有特定权限的进程发送,而有些广播intent在发送之前需要做一些其他动作。当然,如果发送方进程是系统进程、phone进程、shell进程,或者具有root权限的进程,那么必然有权发出广播。

        另外,有时候用户希望发送sticky广播,以便日后注册的receiver可以收到“错过”的sticky广播。要达到这个目的,系统必须在内部维护一张sticky广播表,在具体的实现中,AMS会把广播intent加入mStickyBroadcasts映射表中。mStickyBroadcasts是一张哈希映射表,其key值为intent的action字符串,value值为“与这个action对应的intent数组列表”的引用。当我们发送sticky广播时,新的广播intent要么替换掉intent数组列表中的某项,要么作为一个新项被添加进数组列表,以备日后使用。

        发送广播时,还需要考虑所发送的广播是否需要有序(ordered)递送。而且,receiver本身又分为动态注册和静态声明的,这让我们面对的情况更加复杂。从目前的代码来看,静态receiver一直是按照有序方式递送的,而动态receiver则需要根据ordered参数的值,做不同的处理。当我们需要有序递送时,AMS会把动态receivers和静态receivers合并到一张表中,这样才能依照receiver的优先级,做出正确的处理,此时动态receivers和静态receivers可能呈现一种交错顺序。

        另一方面,有些广播是需要发给特定目标组件的,这个也要加以考虑。

        现在我们来分析broadcastIntentLocked()函数。说得难听点儿,这个函数的实现代码颇有些裹脚布的味道,我们必须耐下性子解读这部分代码。经过一番努力,我们可以将其逻辑大致整理成以下几步:

1) 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记; 
2) 处理和package相关的广播; 
3) 处理其他一些系统广播; 
4) 判断当前是否有权力发出广播; 
5) 如果要发出sticky广播,那么要更新一下系统中的sticky广播列表; 
6) 查询和intent匹配的静态receivers; 
7) 查询和intent匹配的动态receivers; 
8) 尝试向并行receivers递送广播; 
9) 整合(剩下的)并行receivers,以及静态receivers,形成一个串行receivers表; 
10) 尝试逐个向串行receivers递送广播。

        下面我们来详细说这几个部分。

3.1.1 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记

        对应的代码为:

  1. <code class="hljs haskell" style=""><span class="hljs-title" style="">intent</span> = new <span class="hljs-type" style="">Intent</span>(intent);// <span class="hljs-type" style="">By</span> <span class="hljs-keyword" style="">default</span> broadcasts do not go to stopped apps.intent.addFlags(<span class="hljs-type" style="">Intent</span>.<span class="hljs-type" style="">FLAG_EXCLUDE_STOPPED_PACKAGES</span>);</code>  

        为什么intent要添加FLAG_EXCLUDE_STOPPED_PACKAGES标记呢?原因是这样的,在Android 3.1之后,PKMS加强了对“处于停止状态的”应用的管理。如果一个应用在安装后从来没有启动过,或者已经被用户强制停止了,那么这个应用就处于停止状态(stopped state)。为了达到精细调整的目的,Android增加了2个flag:FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,以此来表示intent是否要激活“处于停止状态的”应用。

  1. <code class="hljs gradle" style=""><span class="hljs-comment" style="">/** 
  2.  * If set, this intent will not match any components in packages that 
  3.  * are currently stopped.  If this is not set, then the default behavior 
  4.  * is to include such applications in the result. 
  5.  */</span>  
  6. <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">static</span> <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">int</span> FLAG_EXCLUDE_STOPPED_PACKAGES = <span class="hljs-number" style="">0</span>x00000010;  
  7. <span class="hljs-comment" style="">/** 
  8.  * If set, this intent will always match any components in packages that 
  9.  * are currently stopped.  This is the default behavior when 
  10.  * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these 
  11.  * flags are set, this one wins (it allows overriding of exclude for 
  12.  * places where the framework may automatically set the exclude flag). 
  13.  */</span>  
  14. <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">static</span> <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">int</span> FLAG_INCLUDE_STOPPED_PACKAGES = <span class="hljs-number" style="">0</span>x00000020;</code>  

        从上面的broadcastIntentLocked()函数可以看到,在默认情况下,AMS是不会把intent广播发给“处于停止状态的”应用的。据说Google这样做是为了防止一些流氓软件或病毒干坏事。当然,如果广播的发起者认为自己的确需要广播到“处于停止状态的”应用的话,它可以让intent携带FLAG_INCLUDE_STOPPED_PACKAGES标记,从这个标记的注释可以了解到,如果这两个标记同时设置的话,那么FLAG_INCLUDE_STOPPED_PACKAGES标记会“取胜”,它会覆盖掉framework自动添加的FLAG_EXCLUDE_STOPPED_PACKAGES标记。

3.1.2 处理和package相关的广播

        接下来需要处理一些系统级的“Package广播”,这些主要从PKMS(Package Manager Service)处发来。比如,当PKMS处理APK的添加、删除或改动时,一般会发出类似下面的广播:ACTION_PACKAGE_ADDED、ACTION_PACKAGE_REMOVED、ACTION_PACKAGE_CHANGED、ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE、ACTION_UID_REMOVED。

        AMS必须确保发送“包广播”的发起方具有BROADCAST_PACKAGE_REMOVED权限,如果没有,那么AMS会抛出异常(SecurityException)。接着,AMS判断如果是某个用户id被删除了的话(Intent.ACTION_UID_REMOVED),那么必须把这件事通知给“电池状态服务”(Battery Stats Service)。另外,如果是SD卡等外部设备上的应用不可用了,这常常是因为卡被unmount了,此时PKMS会发出Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE,而AMS则需要把SD卡上的所有包都强制停止(forceStopPackageLocked()),并立即发出另一个“Package广播”——EXTERNAL_STORAGE_UNAVAILABLE。

        如果只是某个外部包被删除或改动了,则要进一步判断intent里是否携带了EXTRA_DONT_KILL_APP额外数据,如果没有携带,说明需要立即强制结束package,否则,不强制结束package。看来有些应用即使在删除或改动了包后,还会在系统(内存)中保留下来并继续运行。另外,如果是删除包的话,此时要发出PACKAGE_REMOVED广播。

3.1.3 处理其他一些系统广播

        broadcastIntentLocked()不但要对“Package广播”进行处理,还要关心其他一些系统广播。比如ACTION_TIMEZONE_CHANGED、ACTION_CLEAR_DNS_CACHE、PROXY_CHANGE_ACTION等等,感兴趣的同学可以自行研究这些广播的意义。

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

        接着,broadcastIntentLocked()会判断当前是否有权力发出广播,代码截选如下:

  1. <code class="hljs lisp" style="">/* 
  2.  * Prevent non-system code (<span class="hljs-name" style="">defined</span> here to be non-persistent 
  3.  * processes) from sending protected broadcasts. 
  4.  */  
  5. if (<span class="hljs-name" style="">callingUid</span> == Process.SYSTEM_UID || callingUid == Process.PHONE_UID  
  6.         || callingUid == Process.SHELL_UID || callingUid == <span class="hljs-number" style="">0</span>)   
  7. {  
  8.     // Always okay.  
  9. }   
  10. else if (<span class="hljs-name" style="">callerApp</span> == null || !callerApp.persistent)   
  11. {  
  12.     try   
  13.     {  
  14.         if (<span class="hljs-name" style="">AppGlobals</span>.getPackageManager().isProtectedBroadcast(<span class="hljs-name" style="">intent</span>.getAction()))   
  15.         {  
  16.             String msg = <span class="hljs-string" style="">"Permission Denial: not allowed to send broadcast "</span>  
  17.                     + intent.getAction() + <span class="hljs-string" style="">" from pid="</span>  
  18.                     + callingPid + <span class="hljs-string" style="">", uid="</span> + callingUid<span class="hljs-comment" style="">;</span>  
  19.             Slog.w(<span class="hljs-name" style="">TAG</span>, msg)<span class="hljs-comment" style="">;</span>  
  20.             throw new SecurityException(<span class="hljs-name" style="">msg</span>)<span class="hljs-comment" style="">;</span>  
  21.         }  
  22.     }   
  23.     catch (<span class="hljs-name" style="">RemoteException</span> e)   
  24.     {  
  25.         Slog.w(<span class="hljs-name" style="">TAG</span>, <span class="hljs-string" style="">"Remote exception"</span>, e)<span class="hljs-comment" style="">;</span>  
  26.         return ActivityManager.BROADCAST_SUCCESS<span class="hljs-comment" style="">;</span>  
  27.     }  
  28. }</code>  

        如果发起方的Uid为SYSTEM_UID、PHONE_UID或SHELL_UID,或者发起方具有root权限,那么它一定有权力发送广播。

        另外,还有一个“保护性广播”的概念,也要考虑进来。网上有一些人询问AndroidManifest.xml中的一级标记<protected-broadcast>是什么意思。简单地说,Google认为有一些广播是只能由系统发送的,如果某个系统级AndroidManifest.xml中写了这个标记,那么在PKMS解析该文件时,就会把“保护性广播”标记中的名字(一般是Action字符串)记录下来。在系统运作起来之后,如果某个不具有系统权限的应用试图发送系统中的“保护性广播”,那么到AMS的broadcastIntentLocked()处就会被拦住,AMS会抛出异常,提示"Permission Denial: not allowed to send broadcast"。

        我们在frameworks/base/core/res/AndroidManifest.xml文件中,可以看到<protected-broadcast>标记的具体写法,截选如下:

 

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

        接着,broadcastIntentLocked()中会判断当前是否在发出sticky广播,如果是的话,必须把广播intent记录下来。

        一开始会判断一下发起方是否具有发出sticky广播的能力,比如说要拥有android.Manifest.permission.BROADCAST_STICKY权限等等。判断合格后,broadcastIntentLocked()会更新AMS里的一张表——mStickyBroadcasts,其大致代码如下:

  1. <code class="hljs cpp" style="">    ArrayList<Intent> <span class="hljs-built_in" style="">list</span> = mStickyBroadcasts.get(intent.getAction());  
  2.     <span class="hljs-keyword" style="">if</span> (<span class="hljs-built_in" style="">list</span> == null)   
  3.     {  
  4.         <span class="hljs-built_in" style="">list</span> = <span class="hljs-keyword" style="">new</span> ArrayList<Intent>();  
  5.         mStickyBroadcasts.put(intent.getAction(), <span class="hljs-built_in" style="">list</span>);  
  6.     }  
  7.     <span class="hljs-keyword" style="">int</span> N = <span class="hljs-built_in" style="">list</span>.size();  
  8.     <span class="hljs-keyword" style="">int</span> i;  
  9.     <span class="hljs-keyword" style="">for</span> (i=<span class="hljs-number" style="">0</span>; i<N; i++)   
  10.     {  
  11.         <span class="hljs-keyword" style="">if</span> (intent.filterEquals(<span class="hljs-built_in" style="">list</span>.get(i)))   
  12.         {  
  13.             <span class="hljs-comment" style="">// This sticky already exists, replace it.</span>  
  14.             <span class="hljs-built_in" style="">list</span>.<span class="hljs-built_in" style="">set</span>(i, <span class="hljs-keyword" style="">new</span> Intent(intent));  
  15.             <span class="hljs-keyword" style="">break</span>;  
  16.         }  
  17.     }  
  18.     <span class="hljs-keyword" style="">if</span> (i >= N)   
  19.     {  
  20.         <span class="hljs-built_in" style="">list</span>.add(<span class="hljs-keyword" style="">new</span> Intent(intent));  
  21.     }</code>  

mStickyBroadcasts的定义是这样的:

  1. <code class="hljs javascript" style="">    final HashMap<<span class="hljs-built_in" style="">String</span>, ArrayList<Intent>> mStickyBroadcasts =  
  2.             <span class="hljs-keyword" style="">new</span> HashMap<<span class="hljs-built_in" style="">String</span>, ArrayList<Intent>>();</code>  

上面代码的filterEquals()函数会比较两个intent的action、data、type、class以及categories等信息,但不会比较extra数据。如果两个intent的action是一样的,但其他信息不同,那么它们在ArrayList<Intent>中会被记成两个不同的intent。而如果发现新发送的intent在ArrayList中已经有个“相等的”旧intent时,则会用新的替掉旧的。

        以后,每当注册新的动态receiver时,注册动作中都会遍历一下mStickyBroadcast表,看哪些intent可以和新receiver的filter匹配,只有匹配的intent才会递送给新receiver,示意图如下:

图中新receiver的filter只对a1和a3这两个action感兴趣,所以遍历时就不会考虑mStickyBroadcast表中的a2表项对应的子表,而a1、a3子表所对应的若干intent中又只有一部分可以和filter匹配,比如a1的intent1以及a3的intent2,所以图中只选择了这两个intent递送给新receiver。

        除了记入mStickyBoradcast表的动作以外,sticky广播和普通广播在broadcastIntentLocked()中的代码是一致的,并没有其他什么不同了。

 

3.1.6 尝试向并行receivers递送广播

        然后broadcastIntentLocked()会尝试向并行receivers递送广播。此时会调用到queue.scheduleBroadcastsLocked()。相关代码截选如下:

  1. <code class="hljs cpp" style=""><span class="hljs-keyword" style="">int</span> NR = registeredReceivers != null ? registeredReceivers.size() : <span class="hljs-number" style="">0</span>;  
  2. <span class="hljs-keyword" style="">if</span> (!ordered && NR > <span class="hljs-number" style="">0</span>)   
  3. {  
  4.     <span class="hljs-comment" style="">// If we are not serializing this broadcast, then send the</span>  
  5.     <span class="hljs-comment" style="">// registered receivers separately so they don't wait for the</span>  
  6.     <span class="hljs-comment" style="">// components to be launched.</span>  
  7.     final BroadcastQueue <span class="hljs-built_in" style="">queue</span> = broadcastQueueForIntent(intent);  
  8.     BroadcastRecord r = <span class="hljs-keyword" style="">new</span> BroadcastRecord(<span class="hljs-built_in" style="">queue</span>, intent, callerApp,  
  9.             callerPackage, callingPid, callingUid, requiredPermission,  
  10.             registeredReceivers, resultTo, resultCode, resultData, <span class="hljs-built_in" style="">map</span>,  
  11.             ordered, sticky, <span class="hljs-literal" style="">false</span>);  
  12.     <span class="hljs-keyword" style="">if</span> (DEBUG_BROADCAST) Slog.v(  
  13.             TAG, <span class="hljs-string" style="">"Enqueueing parallel broadcast "</span> + r);  
  14.     final boolean replaced = replacePending && <span class="hljs-built_in" style="">queue</span>.replaceParallelBroadcastLocked(r);  
  15.     <span class="hljs-keyword" style="">if</span> (!replaced) {  
  16.         <span class="hljs-built_in" style="">queue</span>.enqueueParallelBroadcastLocked(r);  
  17.         <span class="hljs-built_in" style="">queue</span>.scheduleBroadcastsLocked();    <span class="hljs-comment" style="">// 注意这句。。。</span>  
  18.     }  
  19.     registeredReceivers = null;  
  20.     NR = <span class="hljs-number" style="">0</span>;  
  21. }</code>  

简单地说就是,new一个BroadcastRecord节点,并插入BroadcastQueue内的并行处理队列,最后发起实际的广播调度(scheduleBroadcastsLocked())。关于上面代码中的registeredReceivers列表,我们会在后文说明,这里先跳过。

        其实不光并行处理部分需要一个BroadcastRecord节点,串行处理部分也需要BroadcastRecord节点。也就是说,要激发一次广播,AMS必须构造一个或两个BroadcastRecord节点,并将之插入合适的广播队列(mFgBroadcastQueue或mBgBroadcastQueue)。插入成功后,再执行队列的scheduleBroadcastsLocked()动作,进行实际的派发调度。示意图如下:

请注意图中BroadcastRecord节点所携带的节点链。在mParallelBroadcasts表中,每个BroadcastRecord只可能携带BroadcastFilter,因为平行处理的节点只会对应动态receiver,而所有静态receiver只能是串行处理的。另一方面,在mOrderedBroadcasts表中,BroadcastRecord中则既可能携带BroadcastFilter,也可能携带ResolveInfo。这个其实很容易理解,首先,ResolveInfo对应静态receiver,放到这里自不待言,其次,如果用户在发送广播时明确指定要按ordered方式发送的话,那么即使目标方的receiver是动态注册的,它对应的BroadcastFilter也会被强制放到这里。

        好,现在让我们再整合一下思路。BroadcastRecord节点内部的receivers列表,记录着和这个广播动作相关的目标receiver信息,该列表内部的子节点可能是ResolveInfo类型的,也可能是BroadcastFilter类型的。ResolveInfo是从PKMS处查到的静态receiver的描述信息,它的源头是PKMS分析的那些AndroidManifest.xml文件。而BroadcastFilter事实上来自于本文一开始阐述动态receiver时,提到的AMS端的mRegisteredReceivers哈希映射表。现在,我们再画一张示意图:

因为BroadcastRecord里的BroadcastFilter,和AMS的mRegisteredReceivers表中(间接)所指的对应BroadcastFilter是同一个对象,所以我是用虚线将它们连起来的。

         Ok,我们接着看scheduleBroadcastsLocked()动作。scheduleBroadcastsLocked()的代码如下:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

  1. <code class="hljs cpp" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">scheduleBroadcastsLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style="">   
  2. </span></span>{  
  3.     . . . . . .  
  4.     <span class="hljs-keyword" style="">if</span> (mBroadcastsScheduled)   
  5.     {  
  6.         <span class="hljs-keyword" style="">return</span>;  
  7.     }  
  8.     mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, <span class="hljs-keyword" style="">this</span>));  
  9.     mBroadcastsScheduled = <span class="hljs-literal" style="">true</span>;  
  10. }</code>  

发出BROADCAST_INTENT_MSG消息。

        上面用到的mHandler是这样创建的:

  1. <code class="hljs groovy" style=""><span class="hljs-keyword" style="">final</span> Handler mHandler = <span class="hljs-keyword" style="">new</span> Handler()   
  2. {  
  3.     <span class="hljs-keyword" style="">public</span> <span class="hljs-keyword" style="">void</span> handleMessage(Message msg)   
  4.     {  
  5.         <span class="hljs-keyword" style="">switch</span> (msg.what)   
  6.         {  
  7.             <span class="hljs-keyword" style="">case</span> <span class="hljs-string" style="">BROADCAST_INTENT_MSG:</span>   
  8.             {  
  9.                 <span class="hljs-keyword" style="">if</span> (DEBUG_BROADCAST)   
  10.                     Slog.v(TAG, <span class="hljs-string" style="">"Received BROADCAST_INTENT_MSG"</span>);  
  11.                 processNextBroadcast(<span class="hljs-literal" style="">true</span>);  
  12.             }   
  13.             <span class="hljs-keyword" style="">break</span>;  
  14.               
  15.             <span class="hljs-keyword" style="">case</span> <span class="hljs-string" style="">BROADCAST_TIMEOUT_MSG:</span>   
  16.             {  
  17.                 <span class="hljs-keyword" style="">synchronized</span> (mService)   
  18.                 {  
  19.                     broadcastTimeoutLocked(<span class="hljs-literal" style="">true</span>);  
  20.                 }  
  21.             }   
  22.             <span class="hljs-keyword" style="">break</span>;  
  23.         }  
  24.     }  
  25. };</code>  

       也就是说,AMS端会在BroadcastQueue.java中的processNextBroadcast()具体处理广播。

 

3.1.7 整理两个receiver列表

        我们前文已经说过,有些广播是需要有序递送的。为了合理处理“有序递送”和“平行递送”,broadcastIntentLocked()函数内部搞出了两个list:

  1. <code class="hljs php" style=""><span class="hljs-keyword" style="">List</span> receivers = <span class="hljs-keyword" style="">null</span>;  
  2. <span class="hljs-keyword" style="">List</span><BroadcastFilter> registeredReceivers = <span class="hljs-keyword" style="">null</span>;</code>  

其中,receivers主要用于记录“有序递送”的receiver,而registeredReceivers则用于记录与intent相匹配的动态注册的receiver。

        关于这两个list的大致运作是这样的,我们先利用包管理器的queryIntentReceivers()接口,查询出和intent匹配的所有静态receivers,此时所返回的查询结果本身已经排好序了,因此,该返回值被直接赋值给了receivers变量,代码如下:

  1. <code class="hljs ini" style=""><span class="hljs-attr" style="">receivers</span> = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, userId);</code>  

而对于动态注册的receiver信息,就不是从包管理器获取了,这些信息本来就记录在AMS之中,此时只需调用:

  1. <code class="hljs ini" style=""><span class="hljs-attr" style="">registeredReceivers</span> = mReceiverResolver.queryIntent(intent, resolvedType, <span class="hljs-literal" style="">false</span>, userId);</code>  

就可以了。注意,此时返回的registeredReceivers中的子项是没有经过排序的。而关于PKMS的queryIntentReceivers(),我们可以参考PKMS的专题文档,此处不再赘述。

        如果我们要“并行递送”广播, registeredReceivers中的各个receiver会在随后的queue.scheduleBroadcastsLocked()动作中被并行处理掉。如果大家折回头看看向并行receivers递送广播的代码,会发现在调用完queue.scheduleBroadcastsLocked()后,registeredReceivers会被强制赋值成null值。

        如果我们要“串行递送”广播,那么必须考虑把registeredReceivers表合并到receivers表中去。我们知道,一开始receivers列表中只记录了一些静态receiver,这些receiver将会被“有序递送”。现在我们只需再遍历一下registeredReceivers列表,并将其中的每个子项插入到receivers列表的合适地方,就可以合并出一条顺序列表了。当然,如果registeredReceivers已经被设为null了,就无所谓合并了。

        为什么静态声明的receiver只会“有序递送”呢?我想也许和这种receiver的复杂性有关系,因为在需要递送广播时,receiver所属的进程可能还没有启动呢,所以也许会涉及到启动进程的流程,这些都是比较复杂的流程。

        当然,上面所说的是没有明确指定目标组件的情况,如果intent里含有明确的目标信息,那么就不需要调用包管理器的queryIntentReceivers()了,只需new一个ArrayList,并赋值给receivers,然后把目标组件对应的ResolveInfo信息添加进receivers数组列表即可。

 

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

        当receivers列表整理完毕之后,现在要开始尝试逐个向串行receivers递送广播了。正如前文所说,这里要重新new一个新的BroadcastRecord节点:

  1. <code class="hljs cpp" style=""><span class="hljs-keyword" style="">if</span> ((receivers != null && receivers.size() > <span class="hljs-number" style="">0</span>)  
  2.     || resultTo != null)   
  3. {  
  4.     BroadcastQueue <span class="hljs-built_in" style="">queue</span> = broadcastQueueForIntent(intent);  
  5.     BroadcastRecord r = <span class="hljs-keyword" style="">new</span> BroadcastRecord(<span class="hljs-built_in" style="">queue</span>, intent, callerApp,  
  6.             callerPackage, callingPid, callingUid, requiredPermission,  
  7.             receivers, resultTo, resultCode, resultData, <span class="hljs-built_in" style="">map</span>, ordered,  
  8.             sticky, <span class="hljs-literal" style="">false</span>);  
  9.     . . . . . .  
  10.     boolean replaced = replacePending && <span class="hljs-built_in" style="">queue</span>.replaceOrderedBroadcastLocked(r);   
  11.     <span class="hljs-keyword" style="">if</span> (!replaced) {  
  12.         <span class="hljs-built_in" style="">queue</span>.enqueueOrderedBroadcastLocked(r);  
  13.         <span class="hljs-built_in" style="">queue</span>.scheduleBroadcastsLocked();  
  14.     }  
  15. }</code>  

而scheduleBroadcastsLocked()最终会间接导致走到 BroadcastQueue.java中的processNextBroadcast()。这一点和前文所说的“向并行receivers递送广播”的动作基本一致。

        下面我们来看,递送广播动作中最重要的processNextBroadcast()。

 

3.2 最重要的processNextBroadcast()

        从processNextBroadcast()的代码,我们就可以看清楚前面说的“平行广播”、“有序广播”和“动态receiver”、“静态receiver”之间的关系了。

        我们在前文已经说过,所有的静态receiver都是串行处理的,而动态receiver则会按照发广播时指定的方式,进行“并行”或“串行”处理。能够并行处理的广播,其对应的若干receiver一定都已经存在了,不会牵扯到启动新进程的操作,所以可以在一个while循环中,一次性全部deliver。而有序广播,则需要一个一个地处理,其滚动处理的手段是发送事件,也就是说,在一个receiver处理完毕后,会利用广播队列(BroadcastQueue)的mHandler,发送一个BROADCAST_INTENT_MSG事件,从而执行下一次的processNextBroadcast()。

        processNextBroadcast()的代码逻辑大体是这样的:先尝试处理BroadcastQueue中的“平行广播”部分。这需要遍历并行列表(mParallelBroadcasts)的每一个BroadcastRecord以及其中的receivers列表。对于平行广播而言,receivers列表中的每个子节点是个BroadcastFilter。我们直接将广播递送出去即可:

  1. <code class="hljs gradle" style=""><span class="hljs-keyword" style="">while</span> (mParallelBroadcasts.<span class="hljs-keyword" style="">size</span>() > <span class="hljs-number" style="">0</span>)   
  2. {  
  3.     r = mParallelBroadcasts.remove(<span class="hljs-number" style="">0</span>);  
  4.     r.dispatchTime = SystemClock.uptimeMillis();  
  5.     r.dispatchClockTime = System.currentTimeMillis();  
  6.     <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">int</span> N = r.receivers.<span class="hljs-keyword" style="">size</span>();  
  7.     . . . . . .   
  8.     <span class="hljs-keyword" style="">for</span> (<span class="hljs-keyword" style="">int</span> i=<span class="hljs-number" style="">0</span>; i<N; i++)   
  9.     {  
  10.         Object target = r.receivers.get(i);  
  11.         . . . . . .  
  12.         deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, <span class="hljs-keyword" style="">false</span>);  
  13.     }  
  14.     . . . . . .  
  15. }</code>  

 

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

        deliverToRegisteredReceiverLocked()的代码截选如下:

【frameworks/base/services/java/com/android/server/am/BroadcastQueue.java】

  1. <code class="hljs swift" style=""><span class="hljs-keyword" style="">private</span> <span class="hljs-keyword" style="">final</span> void deliverToRegisteredReceiverLocked(<span class="hljs-type" style="">BroadcastRecord</span> r,  
  2.                                                      <span class="hljs-type" style="">BroadcastFilter</span> <span class="hljs-built_in" style="">filter</span>,   
  3.                                                      boolean ordered)   
  4. {  
  5.     . . . . . .  
  6.     . . . . . .  
  7.     <span class="hljs-keyword" style="">if</span> (!skip)   
  8.     {  
  9.         <span class="hljs-keyword" style="">if</span> (ordered)   
  10.         {  
  11.             r.receiver = <span class="hljs-built_in" style="">filter</span>.receiverList.receiver.asBinder();  
  12.             r.curFilter = <span class="hljs-built_in" style="">filter</span>;  
  13.             <span class="hljs-built_in" style="">filter</span>.receiverList.curBroadcast = r;  
  14.             r.state = <span class="hljs-type" style="">BroadcastRecord</span>.<span class="hljs-type" style="">CALL_IN_RECEIVE</span>;  
  15.             <span class="hljs-keyword" style="">if</span> (<span class="hljs-built_in" style="">filter</span>.receiverList.app != null)   
  16.             {  
  17.                 r.curApp = <span class="hljs-built_in" style="">filter</span>.receiverList.app;  
  18.                 <span class="hljs-built_in" style="">filter</span>.receiverList.app.curReceiver = r;  
  19.                 mService.updateOomAdjLocked();  
  20.             }  
  21.         }  
  22.           
  23.             . . . . . .  
  24.             performReceiveLocked(<span class="hljs-built_in" style="">filter</span>.receiverList.app,   
  25.                                  <span class="hljs-built_in" style="">filter</span>.receiverList.receiver,  
  26.                                  new <span class="hljs-type" style="">Intent</span>(r.intent), r.resultCode,  
  27.                                  r.resultData, r.resultExtras,   
  28.                                  r.ordered, r.initialSticky);  
  29.             <span class="hljs-keyword" style="">if</span> (ordered)   
  30.             {  
  31.                 r.state = <span class="hljs-type" style="">BroadcastRecord</span>.<span class="hljs-type" style="">CALL_DONE_RECEIVE</span>;  
  32.             }  
  33.           
  34.         . . . . . .  
  35. }</code>  

 

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">private</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">static</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">performReceiveLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(ProcessRecord app, IIntentReceiver receiver,  
  2.         Intent intent, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode, String data, Bundle extras,  
  3.         </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> ordered, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sticky)</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">throws</span></span></span><span class="hljs-function" style=""> RemoteException   
  4. </span></span>{  
  5.     <span class="hljs-comment" style="">// Send the intent to the receiver asynchronously using one-way binder calls.</span>  
  6.     <span class="hljs-keyword" style="">if</span> (app != <span class="hljs-keyword" style="">null</span> && app.thread != <span class="hljs-keyword" style="">null</span>)   
  7.     {  
  8.         <span class="hljs-comment" style="">// If we have an app thread, do the call through that so it is</span>  
  9.         <span class="hljs-comment" style="">// correctly ordered with other one-way calls.</span>  
  10.         app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,  
  11.                 data, extras, ordered, sticky);  
  12.     }   
  13.     <span class="hljs-keyword" style="">else</span>   
  14.     {  
  15.         receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);  
  16.     }  
  17. }</code>  

终于通过app.thread向用户进程传递语义了。注意scheduleRegisteredReceiver()的receiver参数,它对应的就是前文所说的ReceiverDispatcher的Binder实体——InnerReceiver了。

        总之,当语义传递到用户进程的ApplicationThread以后,走到:

  1. <code class="hljs java" style=""><span class="hljs-comment" style="">// This function exists to make sure all receiver dispatching is</span>  
  2. <span class="hljs-comment" style="">// correctly ordered, since these are one-way calls and the binder driver</span>  
  3. <span class="hljs-comment" style="">// applies transaction ordering per object for such calls.</span>  
  4. <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">scheduleRegisteredReceiver</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(IIntentReceiver receiver, Intent intent,  
  5.         </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode, String dataStr, Bundle extras, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> ordered,  
  6.         </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sticky)</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">throws</span></span></span><span class="hljs-function" style=""> RemoteException   
  7. </span></span>{  
  8.     receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);  
  9. }</code>  

终于走到ReceiverDispatcher的InnerReceiver了:

  1. <code class="hljs java" style=""><span class="hljs-keyword" style="">static</span> <span class="hljs-keyword" style="">final</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">ReceiverDispatcher</span></span></span><span class="hljs-class" style="">   
  2. </span></span>{  
  3.     <span class="hljs-keyword" style="">final</span> <span class="hljs-keyword" style="">static</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">InnerReceiver</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">extends</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">IIntentReceiver</span></span></span><span class="hljs-class" style="">.</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">Stub</span></span></span><span class="hljs-class" style="">   
  4.     </span></span>{  
  5.         . . . . . .  
  6.         . . . . . .  
  7.         <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">performReceive</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(Intent intent, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode,  
  8.                                    String data, Bundle extras,   
  9.                                    </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> ordered, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sticky)</span></span></span><span class="hljs-function" style="">   
  10.         </span></span>{  
  11.             LoadedApk.ReceiverDispatcher rd = mDispatcher.get();  
  12.             . . . . . .  
  13.             <span class="hljs-keyword" style="">if</span> (rd != <span class="hljs-keyword" style="">null</span>) {  
  14.                 rd.performReceive(intent, resultCode, data, extras,  
  15.                                   ordered, sticky);  
  16.             }   
  17.             . . . . . .  
  18.         }  
  19.     }  
  20.     . . . . . .  
  21.     <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">performReceive</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(Intent intent, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode,  
  22.                                String data, Bundle extras,   
  23.                                </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> ordered, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sticky)</span></span></span><span class="hljs-function" style="">   
  24.     </span></span>{  
  25.         . . . . . .  
  26.         Args args = <span class="hljs-keyword" style="">new</span> Args(intent, resultCode, data, extras, ordered, sticky);  
  27.         <span class="hljs-keyword" style="">if</span> (!mActivityThread.post(args)) <span class="hljs-comment" style="">// 请注意这一句!</span>  
  28.         {  
  29.             <span class="hljs-keyword" style="">if</span> (mRegistered && ordered)   
  30.             {  
  31.                 IActivityManager mgr = ActivityManagerNative.getDefault();  
  32.                 <span class="hljs-keyword" style="">if</span> (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,  
  33.                         <span class="hljs-string" style="">"Finishing sync broadcast to "</span> + mReceiver);  
  34.                 args.sendFinished(mgr);  
  35.             }  
  36.         }  
  37.     }  
  38. }</code>  

请注意mActivityThread.post(args)一句,这样,事件泵最终会回调Args参数的run()成员函数:

  1. <code class="hljs java" style=""><span class="hljs-keyword" style="">final</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">class</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">Args</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">extends</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">BroadcastReceiver</span></span></span><span class="hljs-class" style="">.</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">PendingResult</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">implements</span></span></span><span class="hljs-class" style=""> </span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">Runnable</span></span></span><span class="hljs-class" style="">   
  2. </span></span>{  
  3.     . . . . . .  
  4.     . . . . . .  
  5.     <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">run</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style="">   
  6.     </span></span>{  
  7.         <span class="hljs-keyword" style="">final</span> BroadcastReceiver receiver = mReceiver;  
  8.         . . . . . .  
  9.         <span class="hljs-keyword" style="">try</span> {  
  10.             ClassLoader cl =  mReceiver.getClass().getClassLoader();  
  11.             intent.setExtrasClassLoader(cl);  
  12.             setExtrasClassLoader(cl);  
  13.             receiver.setPendingResult(<span class="hljs-keyword" style="">this</span>);  
  14.             receiver.onReceive(mContext, intent);  <span class="hljs-comment" style="">// 回调具体receiver的onReceive()</span>  
  15.         } <span class="hljs-keyword" style="">catch</span> (Exception e) {  
  16.             . . . . . .  
  17.         }  
  18.           
  19.         <span class="hljs-keyword" style="">if</span> (receiver.getPendingResult() != <span class="hljs-keyword" style="">null</span>) {  
  20.             finish();  
  21.         }  
  22.         . . . . . .  
  23.     }  
  24. }</code>  

其中的那句receiver.onReceive(this),正是回调我们具体receiver的onReceive()成员函数的地方。噢,终于看到应用程序员熟悉的onReceive()了。这部分的示意图如下:

3.2.2 静态receiver的递送

        说完动态递送,我们再来看静态递送。对于静态receiver,情况会复杂很多,因为静态receiver所从属的进程有可能还没有运行起来呢。此时BroadcastRecord节点中记录的子列表的节点是ResolveInfo对象。

  1. <code class="hljs tcl" style="">ResolveInfo <span class="hljs-keyword" style="">info</span> = (ResolveInfo)nextReceiver;  
  2. . . . . . .  
  3. r.state = BroadcastRecord.APP_RECEIVE;  
  4. String targetProcess = <span class="hljs-keyword" style="">info</span>.activityInfo.processName;</code>  

那么我们要先找到receiver所从属的进程的进程名。

         processNextBroadcast()中启动进程的代码如下:

  1. <code class="hljs kotlin" style="">ProcessRecord app = mService.getProcessRecordLocked(targetProcess,   
  2. info.activityInfo.applicationInfo.uid);  
  3. . . . . . .  
  4. <span class="hljs-keyword" style="">if</span> (app != <span class="hljs-literal" style="">null</span> && app.thread != <span class="hljs-literal" style="">null</span>)   
  5. {  
  6.     . . . . . .  
  7.     app.addPackage(info.activityInfo.packageName);  
  8.     processCurBroadcastLocked(r, app);  
  9.     <span class="hljs-keyword" style="">return</span>;  
  10.     . . . . . .  
  11. }  
  12. r.curApp = mService.startProcessLocked(targetProcess,  
  13.                                info.activityInfo.applicationInfo, <span class="hljs-literal" style="">true</span>,  
  14.                                r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,  
  15.                                <span class="hljs-string" style="">"broadcast"</span>, r.curComponent,                
  16.                                (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != <span class="hljs-number" style="">0</span>,   
  17.                                <span class="hljs-literal" style="">false</span>)  
  18. . . . . . .  
  19. mPendingBroadcast = r;  
  20. mPendingBroadcastRecvIndex = recIdx;</code>  

        如果目标进程已经存在了,那么app.thread肯定不为null,直接调用processCurBroadcastLocked()即可,否则就需要启动新进程了。启动的过程是异步的,可能很耗时,所以要把BroadcastRecord节点记入mPendingBroadcast。

3.2.2.1 processCurBroadcastLocked()

        我们先说processCurBroadcastLocked()。

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">private</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">final</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">processCurBroadcastLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(BroadcastRecord r,  
  2.                         ProcessRecord app)</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">throws</span></span></span><span class="hljs-function" style=""> RemoteException   
  3. </span></span>{  
  4.     . . . . . .  
  5.     r.receiver = app.thread.asBinder();  
  6.     r.curApp = app;  
  7.     app.curReceiver = r;  
  8.     . . . . . .  
  9.     . . . . . .  
  10.         app.thread.scheduleReceiver(<span class="hljs-keyword" style="">new</span> Intent(r.intent), r.curReceiver,  
  11.               mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),  
  12.               r.resultCode, r.resultData, r.resultExtras, r.ordered);  
  13.         . . . . . .  
  14.         started = <span class="hljs-keyword" style="">true</span>;  
  15.     . . . . . .  
  16. }</code>  

其中最重要的是调用app.thread.scheduleReceiver()的那句。在IApplicationThread接口中,是这样定义scheduleReceiver()函数原型的:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">scheduleReceiver</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(Intent intent, ActivityInfo info,   
  2.                       CompatibilityInfo compatInfo,                        
  3.                       </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode, String data,   
  4.                       Bundle extras, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sync)</span></span></span><span class="hljs-function" style="">   
  5.                       </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">throws</span></span></span><span class="hljs-function" style=""> RemoteException</span></span>;</code>  

其中ActivityInfo info参数,记录着目标receiver的信息。可以看到,递送静态receiver时,是不会用到RecevierDispatcher的。

        用户进程里handleMessage()

  1. <code class="hljs css" style=""><span class="hljs-selector-tag" style="">case</span> <span class="hljs-selector-tag" style="">RECEIVER</span>:  
  2.     <span class="hljs-selector-tag" style="">Trace</span><span class="hljs-selector-class" style="">.traceBegin</span>(<span class="hljs-selector-tag" style="">Trace</span><span class="hljs-selector-class" style="">.TRACE_TAG_ACTIVITY_MANAGER</span>, "<span class="hljs-selector-tag" style="">broadcastReceiveComp</span>");  
  3.     <span class="hljs-selector-tag" style="">handleReceiver</span>((<span class="hljs-selector-tag" style="">ReceiverData</span>)<span class="hljs-selector-tag" style="">msg</span><span class="hljs-selector-class" style="">.obj</span>);  
  4.     <span class="hljs-selector-tag" style="">maybeSnapshot</span>();  
  5.     <span class="hljs-selector-tag" style="">Trace</span><span class="hljs-selector-class" style="">.traceEnd</span>(<span class="hljs-selector-tag" style="">Trace</span><span class="hljs-selector-class" style="">.TRACE_TAG_ACTIVITY_MANAGER</span>);      
  6.     <span class="hljs-selector-tag" style="">break</span>;</code>  

        ActivityThread中,会运用反射机制,创建出BroadcastReceiver对象,而后回调该对象的onReceive()成员函数。

  1. <code class="hljs haskell" style=""><span class="hljs-title" style="">private</span> void handleReceiver(<span class="hljs-type" style="">ReceiverData</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">) </span></span>  
  2. {  
  3.     . . . . . .  
  4.     <span class="hljs-type" style="">IActivityManager</span> mgr = <span class="hljs-type" style="">ActivityManagerNative</span>.getDefault();  
  5.     <span class="hljs-type" style="">BroadcastReceiver</span> receiver;  
  6.     try {  
  7.         java.lang.<span class="hljs-type" style="">ClassLoader</span> cl = packageInfo.getClassLoader();  
  8.         <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">.intent.setExtrasClassLoader(</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">cl</span></span></span><span class="hljs-class" style="">);</span></span>  
  9.         <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">.setExtrasClassLoader(</span><span class="hljs-title" style=""><span class="hljs-class" style=""><span class="hljs-title" style="">cl</span></span></span><span class="hljs-class" style="">);</span></span>  
  10.         receiver = (<span class="hljs-type" style="">BroadcastReceiver</span>)cl.loadClass(component).newInstance();  
  11.     } catch (<span class="hljs-type" style="">Exception</span> e) {  
  12.         . . . . . .  
  13.     }  
  14.     try {  
  15.         . . . . . .  
  16.         receiver.setPendingResult(<span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">);</span></span>  
  17.         receiver.onReceive(context.getReceiverRestrictedContext(), <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">.intent);</span></span>  
  18.     } catch (<span class="hljs-type" style="">Exception</span> e) {  
  19.         . . . . . .  
  20.     } finally {  
  21.         sCurrentBroadcastIntent.set(null);  
  22.     }  
  23.     <span class="hljs-keyword" style="">if</span> (receiver.getPendingResult() != null) {  
  24.         <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">.finish();</span></span>  
  25.     }  
  26. }</code>  
3.2.2.2 必要时启动新进程

        现在我们回过头来看,在目标进程尚未启动的情况下,是如何完成递送的。刚刚我们已经看到调用startProcessLocked()的句子了,只要不出问题,目标进程成功启动后就会调用AMS的attachApplication()。

        有关attachApplication()的详情,请参考其他关于AMS的文档,此处我们只需知道它里面又会调用attachApplicationLocked()函数。

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">private</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">final</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">boolean</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">attachApplicationLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(IApplicationThread thread, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> pid)</span></span></span></span></code>  

attachApplicationLocked()内有这么几句:

  1. <code class="hljs php" style=""><span class="hljs-comment" style="">// Check if a next-broadcast receiver is in this process...</span>  
  2. <span class="hljs-keyword" style="">if</span> (!badApp && isPendingBroadcastProcessLocked(pid)) {  
  3.     <span class="hljs-keyword" style="">try</span> {  
  4.         didSomething = sendPendingBroadcastsLocked(app);  
  5.     } <span class="hljs-keyword" style="">catch</span> (<span class="hljs-keyword" style="">Exception</span> e) {  
  6.         <span class="hljs-comment" style="">// If the app died trying to launch the receiver we declare it 'bad'</span>  
  7.         badApp = <span class="hljs-keyword" style="">true</span>;  
  8.     }  
  9. }</code>  

它们的意思是,如果新启动的进程就是刚刚mPendingBroadcast所记录的进程的话,此时AMS就会执行sendPendingBroadcastsLocked(app)一句。

 

  1. <code class="hljs cpp" style=""><span class="hljs-comment" style="">// The app just attached; send any pending broadcasts that it should receive</span>  
  2. <span class="hljs-function" style=""><span class="hljs-function" style="">boolean </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">sendPendingBroadcastsLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(ProcessRecord app)</span></span></span><span class="hljs-function" style=""> </span></span>{  
  3.     boolean didSomething = <span class="hljs-literal" style="">false</span>;  
  4.     <span class="hljs-keyword" style="">for</span> (BroadcastQueue <span class="hljs-built_in" style="">queue</span> : mBroadcastQueues) {  
  5.         didSomething |= <span class="hljs-built_in" style="">queue</span>.sendPendingBroadcastsLocked(app);  
  6.     }  
  7.     <span class="hljs-keyword" style="">return</span> didSomething;  
  8. }</code>  

BroadcastQueue的sendPendingBroadcastsLocked()函数如下:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">boolean</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">sendPendingBroadcastsLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(ProcessRecord app)</span></span></span><span class="hljs-function" style=""> </span></span>{  
  2.     <span class="hljs-keyword" style="">boolean</span> didSomething = <span class="hljs-keyword" style="">false</span>;  
  3.     <span class="hljs-keyword" style="">final</span> BroadcastRecord br = mPendingBroadcast;  
  4.     <span class="hljs-keyword" style="">if</span> (br != <span class="hljs-keyword" style="">null</span> && br.curApp.pid == app.pid) {  
  5.         <span class="hljs-keyword" style="">try</span> {  
  6.             mPendingBroadcast = <span class="hljs-keyword" style="">null</span>;  
  7.             processCurBroadcastLocked(br, app);  
  8.             didSomething = <span class="hljs-keyword" style="">true</span>;  
  9.         } <span class="hljs-keyword" style="">catch</span> (Exception e) {  
  10.             . . . . . .  
  11.         }  
  12.     }  
  13.     <span class="hljs-keyword" style="">return</span> didSomething;  
  14. }</code>  

可以看到,既然目标进程已经成功启动了,那么mPendingBroadcast就可以赋值为null了。接着,sendPendingBroadcastsLocked()会调用前文刚刚阐述的processCurBroadcastLocked(),其内再通过app.thread.scheduleReceiver(),将语义发送到用户进程,完成真正的广播递送。这部分在上一小节已有阐述,这里就不多说了。

 

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

        我们知道,平行广播的循环很简单,只是在一个while循环里对每个动态receiver执行deliverToRegisteredReceiverLocked()即可。而对有序广播来说,原则上每次processNextBroadcast()只会处理一个BroadcastRecord的一个receiver而已。当然,此时摘下的receiver既有可能是动态注册的,也有可能是静态的。

        对于动态注册的receiver,目标进程处理完广播之后,会间接调用am.finishReceiver()向AMS发出反馈,关于这一步,其实在前面罗列ReceiverDispatcher的performReceive()时已经出现过了,我们再列一下:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">performReceive</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(Intent intent, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode,  
  2.                            String data, Bundle extras,   
  3.                            </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> ordered, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> sticky)</span></span></span><span class="hljs-function" style="">   
  4. </span></span>{  
  5.     . . . . . .  
  6.     Args args = <span class="hljs-keyword" style="">new</span> Args(intent, resultCode, data, extras, ordered, sticky);  
  7.     <span class="hljs-keyword" style="">if</span> (!mActivityThread.post(args))   
  8.     {  
  9.         <span class="hljs-keyword" style="">if</span> (mRegistered && ordered)   
  10.         {  
  11.             IActivityManager mgr = ActivityManagerNative.getDefault();  
  12.             . . . . . .  
  13.             args.sendFinished(mgr);  <span class="hljs-comment" style="">// 请注意这一句!</span>  
  14.         }  
  15.     }  
  16. }</code>  

Args继承于BroadcastReceiver.PendingResult,它调用的sendFinished()就是PendingResult的sendFinished():

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">sendFinished</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(IActivityManager am)</span></span></span><span class="hljs-function" style="">   
  2. </span></span>{  
  3.     <span class="hljs-keyword" style="">synchronized</span> (<span class="hljs-keyword" style="">this</span>) {  
  4.         <span class="hljs-keyword" style="">if</span> (mFinished) {  
  5.             <span class="hljs-keyword" style="">throw</span> <span class="hljs-keyword" style="">new</span> IllegalStateException(<span class="hljs-string" style="">"Broadcast already finished"</span>);  
  6.         }  
  7.         mFinished = <span class="hljs-keyword" style="">true</span>;  
  8.       
  9.         <span class="hljs-keyword" style="">try</span> {  
  10.             <span class="hljs-keyword" style="">if</span> (mResultExtras != <span class="hljs-keyword" style="">null</span>) {  
  11.                 mResultExtras.setAllowFds(<span class="hljs-keyword" style="">false</span>);  
  12.             }  
  13.             <span class="hljs-keyword" style="">if</span> (mOrderedHint) {  
  14.                 am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,  
  15.                         mAbortBroadcast);  
  16.             } <span class="hljs-keyword" style="">else</span> {  
  17.                 <span class="hljs-comment" style="">// This broadcast was sent to a component; it is not ordered,</span>  
  18.                 <span class="hljs-comment" style="">// but we still need to tell the activity manager we are done.</span>  
  19.                 am.finishReceiver(mToken, <span class="hljs-number" style="">0</span>, <span class="hljs-keyword" style="">null</span>, <span class="hljs-keyword" style="">null</span>, <span class="hljs-keyword" style="">false</span>);  
  20.             }  
  21.         } <span class="hljs-keyword" style="">catch</span> (RemoteException ex) {  
  22.         }  
  23.     }  
  24. }</code>  

代码中的am.finishReceiver()会通知AMS,表示用户侧receiver已经处理好了,或者至少告一段落了,请AMS进行下一步动作。

        而对于动态注册的receiver,情况是类似的,最终也是调用am.finishReceiver()向AMS发出回馈的,只不过发起的动作是在ActivityThread的handleReceiver()动作中。前文已经列过这个函数了,大家注意下面的句子即可:

  1. <code class="hljs haskell" style=""><span class="hljs-title" style="">private</span> void handleReceiver(<span class="hljs-type" style="">ReceiverData</span> <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">) </span></span>  
  2. {  
  3.         . . . . . .  
  4.         receiver.setPendingResult(<span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">);</span></span>  
  5.         receiver.onReceive(context.getReceiverRestrictedContext(),  
  6.                 <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">.intent);</span></span>  
  7.         . . . . . .  
  8.     <span class="hljs-keyword" style="">if</span> (receiver.getPendingResult() != null) {  
  9.         <span class="hljs-class" style=""><span class="hljs-keyword" style=""><span class="hljs-class" style=""><span class="hljs-keyword" style="">data</span></span></span><span class="hljs-class" style="">.finish();</span></span>  
  10.     }  
  11. }</code>  

ReceiverData也是继承于BroadcastReceiver.PendingResult的,它调用的finish()是PendingResult的finish():

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">final</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">finish</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">()</span></span></span><span class="hljs-function" style="">   
  2. </span></span>{  
  3.     <span class="hljs-keyword" style="">if</span> (mType == TYPE_COMPONENT) {  
  4.         . . . . . .  
  5.     } <span class="hljs-keyword" style="">else</span> <span class="hljs-keyword" style="">if</span> (mOrderedHint && mType != TYPE_UNREGISTERED) {  
  6.         <span class="hljs-keyword" style="">if</span> (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,  
  7.                 <span class="hljs-string" style="">"Finishing broadcast to "</span> + mToken);  
  8.         <span class="hljs-keyword" style="">final</span> IActivityManager mgr = ActivityManagerNative.getDefault();  
  9.         sendFinished(mgr);  
  10.     }  
  11. }</code>  

此处的sendFinished()内部最终也会调用到am.finishReceiver(),向AMS通告receiver已经处理好了。

         AMS侧在收到finishReceiver语义后,执行:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">finishReceiver</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(IBinder who, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">int</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultCode, String resultData,  
  2.         Bundle resultExtras, </span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> resultAbort)</span></span></span><span class="hljs-function" style="">   
  3. </span></span>{  
  4.     . . . . . .  
  5.     <span class="hljs-keyword" style="">try</span> {  
  6.         <span class="hljs-keyword" style="">boolean</span> doNext = <span class="hljs-keyword" style="">false</span>;  
  7.         BroadcastRecord r = <span class="hljs-keyword" style="">null</span>;  
  8.         <span class="hljs-keyword" style="">synchronized</span>(<span class="hljs-keyword" style="">this</span>) {  
  9.             r = broadcastRecordForReceiverLocked(who);  
  10.             <span class="hljs-keyword" style="">if</span> (r != <span class="hljs-keyword" style="">null</span>) {  
  11.                 doNext = r.queue.finishReceiverLocked(r, resultCode,  
  12.                     resultData, resultExtras, resultAbort, <span class="hljs-keyword" style="">true</span>);  
  13.             }  
  14.         }  
  15.         <span class="hljs-keyword" style="">if</span> (doNext) {  
  16.             r.queue.processNextBroadcast(<span class="hljs-keyword" style="">false</span>);  
  17.         }  
  18.         trimApplications();  
  19.     } <span class="hljs-keyword" style="">finally</span> {  
  20.         Binder.restoreCallingIdentity(origId);  
  21.     }  
  22. }</code>  

可以看到,如有必要,会继续调用processNextBroadcast(),从而完成有序广播的循环处理。

 

3.2.4 说说有序广播的timeout处理

        因为AMS很难知道一次广播究竟能不能完全成功递送出去,所以它必须实现一种“时限机制”。前文在阐述broadcastIntentLocked()时,提到过new一个BroadcastRecord节点,并插入一个BroadcastQueue里的“平行列表”或者“有序列表”。不过当时我们没有太细说那个BroadcastQueue,现在我们多加一点儿说明。

        实际上系统中有两个BroadcastQueue,一个叫做“前台广播队列”,另一个叫“后台广播队列”,在AMS里是这样定义的:

  1. <code class="hljs nginx" style=""><span class="hljs-attribute" style="">BroadcastQueue</span> mFgBroadcastQueue;  
  2. <span class="hljs-attribute" style="">BroadcastQueue</span> mBgBroadcastQueue;</code>  

为什么要搞出两个队列呢?我认为这是因为系统对“广播时限”的要求不同导致的。对于前台广播队列而言,它里面的每个广播必须在10秒之内把广播递送给receiver,而后台广播队列的时限比较宽,只需60秒之内递送到就可以了。具体时限值请看BroadcastQueue的mTimeoutPeriod域。注意,这个10秒或60秒限制是针对一个receiver而言的。比方说“前台广播队列”的某个BroadcastRecord节点对应了3个receiver,那么在处理这个广播节点时,只要能在30秒(3 x 10)之内搞定就可以了。事实上,AMS系统考虑了更多东西,所以它给一个BroadcastRecord的总时限是其所有receiver时限之和的2倍,在此例中就是60秒(2 x 3 x 10)。

        对于平行receiver而言,时限的作用小一点儿,因为动态receiver是直接递送到目标进程的,它不考虑目标端是什么时候处理完这个广播的。

        然而对于有序receiver来说,时限就比较重要了。因为receiver之间必须是串行处理的,也就是说上一个receiver在没处理完时,系统是不会让下一个receiver进行处理的。从processNextBroadcast()的代码来看,在处理有序receiver时,BroadcastRecord里的nextReceiver域会记录“下一个应该处理的receiver”的标号。只有在BroadcastRecord的所有receiver都处理完后,或者BroadcastRecord的处理时间超过了总时限的情况下,系统才会把这个BroadcastRecord节点从队列里删除。因此我们在processNextBroadcast()里看到的获取当前BroadcastRecord的句子是写死为r = mOrderedBroadcasts.get(0)的。

        在拿到当前BroadcastRecord之后,利用nextReceiver值拿到当前该处理的receiver信息:

  1. <code class="hljs cs" style=""><span class="hljs-keyword" style="">int</span> recIdx = r.nextReceiver++;  
  2. . . . . . .  
  3. Object nextReceiver = r.receivers.<span class="hljs-keyword" style="">get</span>(recIdx);</code>  

当然,一开始,nextReceiver的值只会是0,表示第一个receiver有待处理,此时会给BroadcastRecord的dispatchTime域赋值。

  1. <code class="hljs cpp" style=""><span class="hljs-keyword" style="">int</span> recIdx = r.nextReceiver++;  
  2. r.receiverTime = SystemClock.uptimeMillis();<span class="hljs-keyword" style="">if</span> (recIdx == <span class="hljs-number" style="">0</span>) {  
  3.     r.dispatchTime = r.receiverTime;  
  4.     r.dispatchClockTime = System.currentTimeMillis();  
  5.     . . . . . .  
  6. }</code>  

也就是说,dispatchTime的意义是标记实际处理BroadcastRecord的起始时间,那么这个BroadcastRecord所能允许的最大时限值就是:

dispatchTime + 2 * mTimeoutPeriod * 其receiver总数

一旦超过这个时限,而BroadcastRecord又没有处理完,那么就强制结束这个BroadcastRecord节点:

  1. <code class="hljs actionscript" style=""><span class="hljs-keyword" style="">if</span> ((numReceivers > <span class="hljs-number" style="">0</span>) &&  
  2.         (now > r.dispatchTime + (<span class="hljs-number" style="">2</span>*mTimeoutPeriod*numReceivers)))   
  3. {  
  4.     . . . . . .  
  5.     broadcastTimeoutLocked(<span class="hljs-literal" style="">false</span>); <span class="hljs-comment" style="">// forcibly finish this broadcast</span>  
  6.     forceReceive = <span class="hljs-literal" style="">true</span>;  
  7.     r.state = BroadcastRecord.IDLE;  
  8. }</code>  

此处调用的broadcastTimeoutLocked()的参数是boolean fromMsg,表示这个函数是否是在处理“时限消息”的地方调用的,因为当前是在processNextBroadcast()函数里调用broadcastTimeoutLocked()的,所以这个参数为false。从这个参数也可以看出,另一处判断“处理已经超时”的地方是在消息处理机制里,在那个地方,fromMsg参数应该设为true。

        大体上说,每当processNextBroadcast()准备递送receiver时,会调用setBroadcastTimeoutLocked()设置一个延迟消息:

  1. <code class="hljs bash" style="">long timeoutTime = r.receiverTime + mTimeoutPeriod;  
  2. . . . . . .  
  3. <span class="hljs-built_in" style="">set</span>BroadcastTimeoutLocked(timeoutTime);</code>  

setBroadcastTimeoutLocked()的代码如下:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">final</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">setBroadcastTimeoutLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(</span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">long</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> timeoutTime)</span></span></span><span class="hljs-function" style="">   
  2. </span></span>{    <span class="hljs-keyword" style="">if</span> (! mPendingBroadcastTimeoutMessage)   
  3.     {  
  4.         Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, <span class="hljs-keyword" style="">this</span>);  
  5.         mHandler.sendMessageAtTime(msg, timeoutTime);  
  6.         mPendingBroadcastTimeoutMessage = <span class="hljs-keyword" style="">true</span>;  
  7.     }  
  8. }</code>  

只要我们的receiver能及时处理广播,系统就会cancel上面的延迟消息。这也就是说,但凡事件泵的handleMessage()开始处理这个消息,就说明receiver处理超时了。此时,系统会放弃处理这个receiver,并接着尝试处理下一个receiver。

  1. <code class="hljs java" style=""><span class="hljs-keyword" style="">final</span> Handler mHandler = <span class="hljs-keyword" style="">new</span> Handler()   
  2. {  
  3.     <span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">public</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">handleMessage</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(Message msg)</span></span></span><span class="hljs-function" style=""> </span></span>{  
  4.         <span class="hljs-keyword" style="">switch</span> (msg.what)   
  5.         {  
  6.             . . . . . .  
  7.             <span class="hljs-keyword" style="">case</span> BROADCAST_TIMEOUT_MSG:   
  8.             {  
  9.                 <span class="hljs-keyword" style="">synchronized</span> (mService)   
  10.                 {  
  11.                     broadcastTimeoutLocked(<span class="hljs-keyword" style="">true</span>);  
  12.                 }  
  13.             } <span class="hljs-keyword" style="">break</span>;  
  14.         }  
  15.     }  
  16. };</code>  

broadcastTimeoutLocked()的代码截选如下:

  1. <code class="hljs java" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">final</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-keyword" style="">void</span></span></span><span class="hljs-function" style=""> </span><span class="hljs-title" style=""><span class="hljs-function" style=""><span class="hljs-title" style="">broadcastTimeoutLocked</span></span></span><span class="hljs-params" style=""><span class="hljs-function" style=""><span class="hljs-params" style="">(</span></span><span class="hljs-keyword" style=""><span class="hljs-function" style=""><span class="hljs-params" style=""><span class="hljs-keyword" style="">boolean</span></span></span></span><span class="hljs-function" style=""><span class="hljs-params" style=""> fromMsg)</span></span></span><span class="hljs-function" style="">   
  2. </span></span>{  
  3.     <span class="hljs-keyword" style="">if</span> (fromMsg) {  
  4.         mPendingBroadcastTimeoutMessage = <span class="hljs-keyword" style="">false</span>;  
  5.     }  
  6.     <span class="hljs-keyword" style="">if</span> (mOrderedBroadcasts.size() == <span class="hljs-number" style="">0</span>) {  
  7.         <span class="hljs-keyword" style="">return</span>;  
  8.     }  
  9.     <span class="hljs-keyword" style="">long</span> now = SystemClock.uptimeMillis();  
  10.     BroadcastRecord r = mOrderedBroadcasts.get(<span class="hljs-number" style="">0</span>);  
  11.     . . . . . .  
  12.     . . . . . .  
  13.     finishReceiverLocked(r, r.resultCode, r.resultData,  
  14.             r.resultExtras, r.resultAbort, <span class="hljs-keyword" style="">true</span>);  
  15.     scheduleBroadcastsLocked();  
  16.     . . . . . .  
  17. }</code>  

可以看到,当一个receiver超时后,系统会放弃继续处理它,并再次调用scheduleBroadcastsLocked(),尝试处理下一个receiver。

 

4 尾声

        有关Android的广播机制,我们就先说这么多吧。品一杯红茶,说一段代码,管他云山雾罩,任那琐碎冗长,我自冷眼看安卓,瞧他修短随化。


http://my.oschina.net/youranhongcha

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/472194?site
推荐阅读
相关标签
  

闽ICP备14008679号