6.4.2 sendBroadcast流程分析

在SDK中同样定义了几个函数用于发送广播。不过,根据之前的经验,最终和AMS交互的函数可能通过一个接口就能完成。下面来看最简单的广播发送函数sendBroadcast,其代码如下:

[—>ContextImpl.java:sendBroadcast]


public void sendBroadcast(Intent intent){

String resolvedType=intent.resolveTypeIfNeeded(getContentResolver());

try{

intent.setAllowFds(false);

//调用AMS的brodcastIntent,在SDK中定义的广播发送函数最终都会调用它

ActivityManagerNative.getDefault().broadcastIntent(

mMainThread.getApplicationThread(),intent, resolvedType, null,

Activity.RESULT_OK, null, null, null, false, false);

}……

}


AMS的broadcastIntent函数的主要工作将交由AMS的broadcastIntentLocked来完成,故此处直接分析broadcastIntentLocked。

我们分阶段来分析broadcastIntentLocked的工作,先来看第一阶段工作。

1.broadcastIntentLocked分析之一

这部分的代码如下:

[—>ActivityManagerService.java:broadcastIntentLocked]


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){

intent=new Intent(intent);

//为Intent增加FLAG_EXCLUDE_STOPPED_PACKAGES标志,表示该广播不会传递给被STOPPED

//的Package

intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

//处理一些特殊的广播,包括UID_REMOVED, PACKAGE_REMOVED和PACKAGE_ADDED等

final boolean uidRemoved=Intent.ACTION_UID_REMOVED.equals(

intent.getAction());

……//处理特殊的广播,主要和PACKAGE相关,例如接收到PACKAGE_REMOVED广播后,AMS

//需将该Package的组件从相关成员中删除,相关代码可自行阅读

//处理TIME_ZONE变化广播

if(intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction()))

mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);

//处理CLEAR_DNS_CACHE广播

if(intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction()))

mHandler.sendEmptyMessage(CLEAR_DNS_CACHE);

//处理PROXY_CHANGE广播

if(Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())){

ProxyProperties proxy=intent.getParcelableExtra("proxy");

mHandler.sendMessage(mHandler.obtainMessage(

UPDATE_HTTP_PROXY, proxy));

}


由以上代码可知,broadcastIntentLocked第一阶段的工作主要是处理一些特殊的广播消息。下面来看broadcastIntentLocked第二阶段的工作。

2.broadcastIntentLocked分析之二

这部分的代码如下:

[—>ActivityManagerService.java:broadcastIntentLocked]


//处理发送sticky广播的情况

if(sticky){

……//检查发送进程是否有BROADCAST_STICKY权限

if(requiredPermission!=null){

//在发送Sticky广播的时候,不能携带权限信息

return BROADCAST_STICKY_CANT_HAVE_PERMISSION;

}

//在发送Stikcy广播的时候,也不能指定特定的接收对象

if(intent.getComponent()!=null)……//抛出异常

//将这个Sticky的Intent保存到mStickyBroadcasts中

ArrayList<Intent>list=mStickyBroadcasts.get(intent.getAction());

if(list==null){

list=new ArrayList<Intent>();

mStickyBroadcasts.put(intent.getAction(),list);

}

……//如果list中已经有该Intent,则直接用新的Intent替换旧的Intent

//否则将该Intent加入到list中保存

if(i>=N)list.add(new Intent(intent));

}

//定义两个变量,其中receivers用于保存匹配该Intent的所有广播接收者,包括静态或动态

//注册的广播接收者,registeredReceivers用于保存符合该Intent的所有动态注册者

List receivers=null;

List<BroadcastFilter>registeredReceivers=null;

try{

if(intent.getComponent()!=null){

……//如果指定了接收者,则从PKMS中查询它的信息

}else{

//FLAG_RECEIVER_REGISTERED_ONLY标签表明该广播只能发给动态注册者

if((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)==0){

//如果没有设置前面的标签,则需要查询PKMS,获得那些在AndroidManifest.xml

//中声明的广播接收者,即静态广播接收者的信息,并保存到receivers中

receivers=

AppGlobals.getPackageManager().queryIntentReceivers(

intent, resolvedType, STOCK_PM_FLAGS);

}

//再从AMS的mReceiverResolver中查询符合条件的动态广播接收者

registeredReceivers=mReceiverResolver.queryIntent(intent,

resolvedType, false);

}

}……

/*

判断该广播是否设置了REPLACE_PENDING标签。如果设置了该标签,并且之前的那个Intent还没

有被处理,则可以用新的Intent替换旧的Intent。这么做的目的是减少不必要的广播发送,但笔

者感觉,这个标签的作用并不靠谱,因为只有旧的Intent没被处理的时候,它才能被替换。因为旧

Intent被处理的时间不能确定,所以不能保证广播发送者的一番“好意”能够实现。因此,在发送广

播时,千万不要以为设置了该标志就一定能节约不必要的广播发送。

*/

final boolean replacePending=

(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING)!=0;

//先处理动态注册的接收者

int NR=registeredReceivers!=null?registeredReceivers.size():0;

//如果此次广播为非串行化发送,并且符合条件的动态注册接收者个数不为零

if(!ordered&&NR>0){

//创建一个BroadcastRecord对象即可,注意,一个BroadcastRecord对象可包括所有的

//接收者(可参考图6-19)

BroadcastRecord r=new BroadcastRecord(intent, callerApp,

callerPackage, callingPid, callingUid, requiredPermission,

registeredReceivers, resultTo, resultCode, resultData, map,

ordered, sticky, false);

boolean replaced=false;

if(replacePending){

……//从mParalledBroadcasts中查找是否有旧的Intent,如果有就替代它,并设置

//replaced为true

}

if(!replaced){//如果没有被替换,则保存到mParallelBroadcasts中

mParallelBroadcasts.add(r);

scheduleBroadcastsLocked();//调度一次广播发送

}

//至此,动态注册的广播接收者已处理完毕,设置registeredReceivers为null

registeredReceivers=null;//

NR=0;

}


broadcastIntentLocked第二阶段的工作有两项:

查询满足条件的动态广播接收者及静态广播接收者。

当本次广播不为ordered时,需要尽快发送该广播。另外,非ordered的广播都被AMS保存在mParallelBroadcasts中。

3.broadcastIntentLocked分析之三

下面来看broadcastIntentLocked最后一阶段的工作,其代码如下:

[—>ActivityManagerService.java:broadcastIntentLocked]


int ir=0;

if(receivers!=null){

String skipPackages[]=null;

……//处理PACKAGE_ADDED的Intent,系统不希望有些应用程序一安装就启动。

//实现这一目的的工作原理即是在该程序内部监听PACKAGE_ADDED广播。如果系统

//没有这一招防备,则PKMS安装完程序后所发送的PAKCAGE_ADDED消息将触发该应用的启动

……//处理ACTION_EXTERNAL_APPLICATIONS_AVAILABLE广播

……//将动态注册的接收者registeredReceivers的元素合并到receivers中去

//处理完毕后,所有的接收者(无论动态还是静态注册的)都存储到receivers变量中了

if((receivers!=null&&receivers.size()>0)||resultTo!=null){

//创建一个BroadcastRecord对象,注意它的receivers中包括所有的接收者

BroadcastRecord r=new BroadcastRecord(intent, callerApp,

callerPackage, callingPid, callingUid, requiredPermission,

receivers, resultTo, resultCode, resultData, map, ordered,

sticky, false);

boolean replaced=false;

if(replacePending){

……//替换mOrderedBroadcasts中旧的Intent

}//

if(!replaced){//如果没有替换,则添加该条广播记录到mOrderedBroadcasts中

mOrderedBroadcasts.add(r);

scheduleBroadcastsLocked();//调度AMS进行广播发送工作

}

}

return BROADCAST_SUCCESS;

}


由以上代码可知,AMS将动态注册者和静态注册者都合并到receivers中去了。注意,如果本次广播不是ordered,那么表明动态注册者就已经在第二阶段工作中被处理了。因此在合并时,将不会有动态注册者被加到receivers中。最终所创建的广播记录存储在mOrderedBroadcasts中,也就是说,不管是否串行化发送,静态接收者对应的广播记录都将保存在mOrderedBroadcasts中。

为什么不将它们保存在mParallelBroadcasts中呢?结合代码会发现,保存在mParallel-Broadcasts中的BroadcastRecord所包含的都是动态注册的广播接收者信息,这是因为动态接收者所在的进程是已经存在的(如果该进程不存在,如何去注册动态接收者呢?),而静态广播接收者就不能保证它已经和一个进程绑定在一起了(静态广播接收者此时可能还仅仅是在AndroidManifest.xml中声明的一个信息,相应的进程并未创建和启动)。根据前一节分析的Activity启动流程,AMS还需要进行创建应用进程,初始化Android运行环境等一系列复杂的操作。读到后面内容时你会发现,AMS将在一个循环中逐条处理mParallelBroadcasts中的成员(即派发给接收者)。如果将静态广播接收者也保存到mParallelBroadcasts中,会有什么后果呢?假设这些静态接收者所对应的进程全部未创建和启动,那么AMS将在那个循环中创建多个进程!这样,系统压力一下就上去了。所以,对于静态注册者,它们对应的广播记录都被放到mOrderedBroadcasts中保存。AMS在处理这类广播信息时,一个进程一个进程地处理,只有处理完一个接收者,才继续下一个接收者。这种做法的好处是,避免了惊群效应的出现,坏处则是延时较长。

下面进入AMS的BROADCAST_INTENT_MSG消息处理函数,看看情况是否如上所说。