💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
在SDK中同样定义了好几个函数用于发送广播。不过,根据之前的经验,最终和AMS交互的函数可能通过一个接口就能完成。来看最简单的广播发送函数sendBroadcast,其代码如下: **ContextImpl.java::sendBroadcast** ~~~ public void sendBroadcast(Intent intent) { StringresolvedType = 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。 1. broadcastIntentLocked分析 我们分阶段来分析broadcastIntentLocked的工作,先来看第一阶段工作。 (1) broadcastIntentLocked分析之一 **ActivityManagerService.java::broadcastIntentLocked** ~~~ private final int broadcastIntentLocked(ProcessRecordcallerApp, StringcallerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundlemap, 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等 finalboolean 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广播的时候,不能携带权限信息 returnBROADCAST_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的所有动态注册者 Listreceivers = 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) { StringskipPackages[] = 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); booleanreplaced = false; if (replacePending) { ......//替换mOrderedBroadcasts中旧的Intent }// if(!replaced) {//如果没有替换,则添加该条广播记录到mOrderedBroadcasts中 mOrderedBroadcasts.add(r); scheduleBroadcastsLocked();//调度AMS进行广播发送工作 } } returnBROADCAST_SUCCESS; } ~~~ 由以上代码可知,AMS将动态注册者和静态注册者都合并到receivers中去了。注意,如果本次广播不是ordered,那么表明动态注册者就已经在第二阶段工作中被处理了。因此在合并时,将不会有动态注册者被加到receivers中。最终所创建的广播记录存储在mOrderedBroadcasts中,也就是说,不管是否串行化发送,静态接收者对应的广播记录都将保存在mOrderedBroadcasts中。 为什么不将它们保存在mParallelBroadcasts中呢? 结合代码会发现,保存在mParallelBroadcasts中的BroadcastRecord所包含的都是动态注册的广播接收者信息,这是因为动态接收者所在的进程是已经存在的(如果该进程不存在,如何去注册动态接收者呢?),而静态广播接收者就不能保证它已经和一个进程绑定在一起了(静态广播接收者此时可能还仅仅是在AndroidManifest.xml中声明的一个信息,相应的进程并未创建和启动)。根据前一节所分析的Activity启动流程,AMS还需要进行创建应用进程,初始化Android运行环境等一系列复杂的操作。读到后面内容时会发现,AMS将在一个循环中逐条处理mParallelBroadcasts中的成员(即派发给接收者)。如果将静态广播接收者也保存到mParallelBroadcasts中,会有什么后果呢?假设这些静态接收者所对应的进程全部未创建和启动,那么AMS将在那个循环中创建多个进程!结果让人震惊,系统压力一下就上去了。所以,对于静态注册者,它们对应的广播记录都被放到mOrderedBroadcasts中保存。AMS在处理这类广播信息时,一个进程一个进程地处理,只有处理完一个接收者,才继续下一个接收者。这种做法的好处是,避免了惊群效应的出现,坏处则是延时较长。 下面进入AMS的BROADCAST_INTENT_MSG消息处理函数,看看情况是否如上所说。