广播(Broadcast内部机制讲解)【续】

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
2
3
4
5
mContext = getApplicationContext(); 
Intent intent =  new  Intent();  
intent.setAction( "com.android.xxxxx" );  
intent.setFlags( 1 );  
mContext.sendBroadcast(intent);

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public  void  sendBroadcast(Intent intent) 
{
     String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());    
     try 
     {
         intent.setAllowFds( false );
         ActivityManagerNative.getDefault().broadcastIntent(
             mMainThread.getApplicationThread(), intent, resolvedType,  null ,
             Activity.RESULT_OK,  null null null false false ,
             Binder.getOrigCallingUser());
     catch  (RemoteException e) {
     }
}

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

 

3.1 AMS一侧的broadcastIntentLocked()

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

?
1
2
3
4
5
6
7
8
9
private  final  int  broadcastIntentLocked(ProcessRecord callerApp,
                                         String callerPackage, 
                                         Intent intent, String resolvedType,
                                         IIntentReceiver resultTo,  int  resultCode, 
                                         String resultData,
                                         Bundle map, String requiredPermission,
                                         boolean  ordered,  boolean  sticky, 
                                         int  callingPid,  int  callingUid,                   
                                         int  userId)

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
intent =  new  Intent(intent); // By default broadcasts do not go to stopped apps.intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

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

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

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

        如果发起方的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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
     ArrayList<Intent> list = mStickyBroadcasts.get(intent.getAction());
     if  (list ==  null
     {
         list =  new  ArrayList<Intent>();
         mStickyBroadcasts.put(intent.getAction(), list);
     }
     int  N = list.size();
     int  i;
     for  (i= 0 ; i<N; i++) 
     {
         if  (intent.filterEquals(list.get(i))) 
         {
             // This sticky already exists, replace it.
             list.set(i,  new  Intent(intent));
             break ;
         }
     }
     if  (i >= N) 
     {
         list.add( new  Intent(intent));
     }

mStickyBroadcasts的定义是这样的:

?
1
2
     final  HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
             new  HashMap<String, ArrayList<Intent>>();

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

简单地说就是,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
2
3
4
5
6
7
8
9
10
public  void  scheduleBroadcastsLocked() 
{
     . . . . . .
     if  (mBroadcastsScheduled) 
     {
         return ;
     }
     mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG,  this ));
     mBroadcastsScheduled =  true ;
}

发出BROADCAST_INTENT_MSG消息。

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

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

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

 

3.1.7 整理两个receiver列表

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

?
1
2
List receivers =  null ;
List<BroadcastFilter> registeredReceivers =  null ;

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

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

?
1
receivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, userId);

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

?
1
registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType,  false , userId);

就可以了。注意,此时返回的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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if  ((receivers !=  null  && receivers.size() >  0 )
     || resultTo !=  null
{
     BroadcastQueue queue = broadcastQueueForIntent(intent);
     BroadcastRecord r =  new  BroadcastRecord(queue, intent, callerApp,
             callerPackage, callingPid, callingUid, requiredPermission,
             receivers, resultTo, resultCode, resultData, map, ordered,
             sticky,  false );
     . . . . . .
     boolean  replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
     if  (!replaced) {
         queue.enqueueOrderedBroadcastLocked(r);
         queue.scheduleBroadcastsLocked();
     }
}

而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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while  (mParallelBroadcasts.size() >  0
{
     r = mParallelBroadcasts.remove( 0 );
     r.dispatchTime = SystemClock.uptimeMillis();
     r.dispatchClockTime = System.currentTimeMillis();
     final  int  N = r.receivers.size();
     . . . . . . 
     for  ( int  i= 0 ; i<N; i++) 
     {
         Object target = r.receivers.get(i);
         . . . . . .
         deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target,  false );
     }
     . . . . . .
}

 

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

        deliverToRegisteredReceiverLocked()的代码截选如下:

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

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

 

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

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

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

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

终于走到ReceiverDispatcher的InnerReceiver了:

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

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

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

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

3.2.2 静态receiver的递送

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

?
1
2
3
4
ResolveInfo info = (ResolveInfo)nextReceiver;
. . . . . .
r.state = BroadcastRecord.APP_RECEIVE;
String targetProcess = info.activityInfo.processName;

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

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

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

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

3.2.2.1 processCurBroadcastLocked()

        我们先说processCurBroadcastLocked()。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private  final  void  processCurBroadcastLocked(BroadcastRecord r,
                         ProcessRecord app)  throws  RemoteException 
{
     . . . . . .
     r.receiver = app.thread.asBinder();
     r.curApp = app;
     app.curReceiver = r;
     . . . . . .
     . . . . . .
         app.thread.scheduleReceiver( new  Intent(r.intent), r.curReceiver,
               mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
               r.resultCode, r.resultData, r.resultExtras, r.ordered);
         . . . . . .
         started =  true ;
     . . . . . .
}

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

?
1
2
3
4
5
void  scheduleReceiver(Intent intent, ActivityInfo info, 
                       CompatibilityInfo compatInfo,                      
                       int  resultCode, String data, 
                       Bundle extras,  boolean  sync) 
                       throws  RemoteException;

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

        用户进程里handleMessage()

?
1
2
3
4
5
6
case  RECEIVER:
     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,  "broadcastReceiveComp" );
     handleReceiver((ReceiverData)msg.obj);
     maybeSnapshot();
     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);    
     break ;

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

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

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

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

?
1
private  final  boolean  attachApplicationLocked(IApplicationThread thread,  int  pid)

attachApplicationLocked()内有这么几句:

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

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

 

?
1
2
3
4
5
6
7
8
// The app just attached; send any pending broadcasts that it should receive
boolean  sendPendingBroadcastsLocked(ProcessRecord app) {
     boolean  didSomething =  false ;
     for  (BroadcastQueue queue : mBroadcastQueues) {
         didSomething |= queue.sendPendingBroadcastsLocked(app);
     }
     return  didSomething;
}

BroadcastQueue的sendPendingBroadcastsLocked()函数如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public  boolean  sendPendingBroadcastsLocked(ProcessRecord app) {
     boolean  didSomething =  false ;
     final  BroadcastRecord br = mPendingBroadcast;
     if  (br !=  null  && br.curApp.pid == app.pid) {
         try  {
             mPendingBroadcast =  null ;
             processCurBroadcastLocked(br, app);
             didSomething =  true ;
         catch  (Exception e) {
             . . . . . .
         }
     }
     return  didSomething;
}

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

 

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public  void  performReceive(Intent intent,  int  resultCode,
                            String data, Bundle extras, 
                            boolean  ordered,  boolean  sticky) 
{
     . . . . . .
     Args args =  new  Args(intent, resultCode, data, extras, ordered, sticky);
     if  (!mActivityThread.post(args)) 
     {
         if  (mRegistered && ordered) 
         {
             IActivityManager mgr = ActivityManagerNative.getDefault();
             . . . . . .
             args.sendFinished(mgr);   // 请注意这一句!
         }
     }
}

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
private  void  handleReceiver(ReceiverData data) 
{
         . . . . . .
         receiver.setPendingResult(data);
         receiver.onReceive(context.getReceiverRestrictedContext(),
                 data.intent);
         . . . . . .
     if  (receiver.getPendingResult() !=  null ) {
         data.finish();
     }
}

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

?
1
2
3
4
5
6
7
8
9
10
11
public  final  void  finish() 
{
     if  (mType == TYPE_COMPONENT) {
         . . . . . .
     else  if  (mOrderedHint && mType != TYPE_UNREGISTERED) {
         if  (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                 "Finishing broadcast to "  + mToken);
         final  IActivityManager mgr = ActivityManagerNative.getDefault();
         sendFinished(mgr);
     }
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  void  finishReceiver(IBinder who,  int  resultCode, String resultData,
         Bundle resultExtras,  boolean  resultAbort) 
{
     . . . . . .
     try  {
         boolean  doNext =  false ;
         BroadcastRecord r =  null ;
         synchronized ( this ) {
             r = broadcastRecordForReceiverLocked(who);
             if  (r !=  null ) {
                 doNext = r.queue.finishReceiverLocked(r, resultCode,
                     resultData, resultExtras, resultAbort,  true );
             }
         }
         if  (doNext) {
             r.queue.processNextBroadcast( false );
         }
         trimApplications();
     finally  {
         Binder.restoreCallingIdentity(origId);
     }
}

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

 

3.2.4 说说有序广播的timeout处理

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

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

?
1
2
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;

为什么要搞出两个队列呢?我认为这是因为系统对“广播时限”的要求不同导致的。对于前台广播队列而言,它里面的每个广播必须在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
2
3
int  recIdx = r.nextReceiver++;
. . . . . .
Object nextReceiver = r.receivers.get(recIdx);

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

?
1
2
3
4
5
6
int  recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis(); if  (recIdx ==  0 ) {
     r.dispatchTime = r.receiverTime;
     r.dispatchClockTime = System.currentTimeMillis();
     . . . . . .
}

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

dispatchTime + 2 * mTimeoutPeriod * 其receiver总数

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

?
1
2
3
4
5
6
7
8
if  ((numReceivers >  0 ) &&
         (now > r.dispatchTime + ( 2 *mTimeoutPeriod*numReceivers))) 
{
     . . . . . .
     broadcastTimeoutLocked( false );  // forcibly finish this broadcast
     forceReceive =  true ;
     r.state = BroadcastRecord.IDLE;
}

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

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

?
1
2
3
long  timeoutTime = r.receiverTime + mTimeoutPeriod;
. . . . . .
setBroadcastTimeoutLocked(timeoutTime);

setBroadcastTimeoutLocked()的代码如下:

?
1
2
3
4
5
6
7
8
final  void  setBroadcastTimeoutLocked( long  timeoutTime) 
{     if  (! mPendingBroadcastTimeoutMessage) 
     {
         Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG,  this );
         mHandler.sendMessageAtTime(msg, timeoutTime);
         mPendingBroadcastTimeoutMessage =  true ;
     }
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
final  Handler mHandler =  new  Handler() 
{
     public  void  handleMessage(Message msg) {
         switch  (msg.what) 
         {
             . . . . . .
             case  BROADCAST_TIMEOUT_MSG: 
             {
                 synchronized  (mService) 
                 {
                     broadcastTimeoutLocked( true );
                 }
             break ;
         }
     }
};

broadcastTimeoutLocked()的代码截选如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final  void  broadcastTimeoutLocked( boolean  fromMsg) 
{
     if  (fromMsg) {
         mPendingBroadcastTimeoutMessage =  false ;
     }
     if  (mOrderedBroadcasts.size() ==  0 ) {
         return ;
     }
     long  now = SystemClock.uptimeMillis();
     BroadcastRecord r = mOrderedBroadcasts.get( 0 );
     . . . . . .
     . . . . . .
     finishReceiverLocked(r, r.resultCode, r.resultData,
             r.resultExtras, r.resultAbort,  true );
     scheduleBroadcastsLocked();
     . . . . . .
}

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值