多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
1. ContextImpl registerReceiver分析 registerReceiver函数用于注册一个动态广播接收者,该函数在Context.java中声明。根据本章前面对Context家族的介绍(参考图6-3),其功能最终将通过ContextImpl类的registerReceiver函数来完成,可直接去看ContextImpl是如何实现此函数的。在SDK中一共定义了两个同名的registerReceiver函数,其代码如下: **ContextImpl.java::registerReceiver** ~~~ /* 在SDK中输出该函数,这也是最常用的函数。当广播到来时,BroadcastReceiver对象的onReceive 函数将在主线程中被调用 */ public Intent registerReceiver(BroadcastReceiverreceiver, IntentFilter filter) { returnregisterReceiver(receiver, filter, null, null); } /* 功能和前面类似,但增加了两个参数,分别是broadcastPermission和scheduler,作用有 两个: 其一:对广播者的权限增加了控制,只有拥有相应权限的广播者发出的广播才能被此接收者接收 其二:BroadcastReceiver对象的onReceiver函数可调度到scheduler所在的线程中执行 */ publicIntent registerReceiver(BroadcastReceiver receiver, IntentFilterfilter, String broadcastPermission, Handler scheduler) { /* 注意,下面所调用函数的最后一个参数为getOuterContext的返回值。前面曾说过,ContextImpl为Context家族中真正干活的对象,而它对外的代理人可以是Application和Activity等, getOuterContext就返回这个对外代理人。一般在Activity中调用registerReceiver函数,故此处getOuterContext返回的对外代理人的类型就是Activity。 */ returnregisterReceiverInternal(receiver, filter, broadcastPermission, scheduler, getOuterContext()); } ~~~ 殊途同归,最终的功能由registerReceiverInternal来完成,其代码如下: **ContextImpl.java::registerReceiverInternal** ~~~ privateIntent registerReceiverInternal(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context) { IIntentReceiver rd = null; if(receiver != null) { //①准备一个IIntentReceiver对象 if(mPackageInfo != null && context != null) { //如果没有设置scheduler,则默认使用主线程的Handler if (scheduler == null) scheduler= mMainThread.getHandler(); //通过getReceiverDispatcher函数得到一个IIntentReceiver类型的对象 rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { if (scheduler == null) scheduler= mMainThread.getHandler(); //直接创建LoadedApk.ReceiverDispatcher对象 rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver(); }//if (mPackageInfo != null && context != null)结束 }// if(receiver != null)结束 try { //②调用AMS的registerReceiver函数 return ActivityManagerNative.getDefault().registerReceiver( mMainThread.getApplicationThread(),mBasePackageName, rd, filter,broadcastPermission); } ...... } ~~~ 以上代码列出了两个关键点:其一是准备一个IIntentReceiver对象;其二是调用AMS的registerReceiver函数。 先来看IIntentReceiver,它是一个Interface,图6-17列出了和它相关的成员图谱。 :-: ![](http://img.blog.csdn.net/20150803123322284?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-17 IIntentReceiver相关成员示意图 由图6-17可知: - BroadcastReceiver内部有一个PendingResult类。该类是Android 2.3以后新增的,用于异步处理广播消息。例如,当BroadcastReceiver收到一个广播时,其onReceive函数将被调用。一般都是在该函数中直接处理该广播。不过,当该广播处理比较耗时,还可采用异步的方式进行处理,即先调用BroadcastReceiver的goAsync函数得到一个PendingResult对象,然后将该对象放到工作线程中去处理,这样onReceive就可以立即返回而不至于耽误太长时间(这一点对于onReceive函数被主线程调用的情况尤为有用)。在工作线程处理完这条广播后,需调用PendingResult的finish函数来完成整个广播的处理流程。 - 广播由AMS发出,而接收及处理工作却在另外一个进程中进行,整个过程一定涉及进程间通信。在图6-17中,虽然在BroadcastReceiver中定义了一个广播接收者,但是它与Binder没有有任何关系,故其并不直接参与进程间通信。与之相反,IIntentReceiver接口则和Binder有密切关系,故可推测广播的接收是由IIntentReceiver接口来完成的。确实,在整个流程中,首先接收到来自AMS的广播的将是该接口的Bn端,即LoadedApk.ReceiverDispather的内部类InnerReceiver。 接收广播的处理将放到本节最后再来分析,下面先来看AMS 的registerReceiver函数。 2. AMS的registerReceiver分析 AMS的registerReceiver函数比较简单,但是由于其中将出现一些新的变量类型和成员,因此接下来按分两部分进行分析。 (1) registerReceiver分析之一 registerReceiver的返回值是一个Intent,它指向一个匹配过滤条件(由filter参数指明)的Sticky Intent。即使有多个符合条件的Intent,也只返回一个。 **ActivityManagerService.java::registerReceiver** ~~~ public Intent registerReceiver(IApplicationThreadcaller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission) { synchronized(this) { ProcessRecord callerApp = null; if(caller != null) { callerApp = getRecordForAppLocked(caller); ....... //如果callerApp为null,则抛出异常,即系统不允许未登记照册的进程注册 //动态广播接收者 //检查调用进程是否有callerPackage的信息,如果没有,也抛异常 if(callerApp.info.uid != Process.SYSTEM_UID && !callerApp.pkgList.contains(callerPackage)){ throw new SecurityException(......); } }......//if(caller != null)判断结束 List allSticky = null; //下面这段代码的功能是从系统中所有Sticky Intent中查询匹配IntentFilter的Intent, //匹配的Intent保存在allSticky中 Iterator actions = filter.actionsIterator(); if(actions != null) { while (actions.hasNext()) { String action = (String)actions.next(); allSticky = getStickiesLocked(action, filter, allSticky); } } ...... //如果存在sticky的Intent,则选取第一个Intent作为本函数的返回值 Intentsticky = allSticky != null ? (Intent)allSticky.get(0) : null; //如果没有设置接收者,则直接返回sticky 的intent if(receiver == null) return sticky; //新的数据类型ReceiverList及mRegisteredReceivers成员变量,见下文的解释 //receiver.asBinder将返回IIntentReceiver的Bp端 ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder()); //如果是首次调用,则此处rl的值将为null if (rl== null) { rl =new ReceiverList(this, callerApp, Binder.getCallingPid(), Binder.getCallingUid(),receiver); if (rl.app != null) { rl.app.receivers.add(rl); }else { try { //监听广播接收者所在进程的死亡消息 receiver.asBinder().linkToDeath(rl, 0); }...... rl.linkedToDeath = true; }// if(rl.app != null)判断结束 //将rl保存到mRegisterReceivers中 mRegisteredReceivers.put(receiver.asBinder(), rl); } //新建一个BroadcastFilter对象 BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); rl.add(bf);//将其保存到rl中 // mReceiverResolver成员变量,见下文解释 mReceiverResolver.addFilter(bf); ~~~ 以上代码的流程倒是很简单,不过其中出现的几个成员变量和数据类型却严重阻碍了我们的思维活动。先解决它们,BroadcastFilter及相关成员变量如图6-18所示。 :-: ![](http://img.blog.csdn.net/20150803123341456?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-18 BroadcastFilter及相关成员变量 结合代码,对图6-18中各数据类型和成员变量的作用及关系的解释如下: - 在AMS中,BroadcastReceiver的过滤条件由BroadcastFilter表示,该类从IntentFilter派生。由于一个BroadcastReceiver可设置多个过滤条件(即多次为同一个BroadcastReceiver对象调用registerReceiver函数以设置不同的过滤条件),故AMS使用ReceiverList(从ArrayList<BroadcastFilter>派生)这种数据类型来表达这种一对多的关系。 - ReceiverList除了能存储多个BroadcastFilter外,还应该有成员指向某一个具体BroadcastReceiver,否则如何知道到底是哪个BroadcastReceiver设置的过滤条件呢?前面说过,BroadcastReceiver接收广播是通过IIntentReceiver接口进行的,故ReceiverList中有receiver成员变量指向IIntentReceiver。 - AMS提供mRegisterReceivers用于保存IIntentReceiver和对应ReceiverList的关系。此外,AMS还提供mReceiverResolver变量用于存储所有动态注册的BroadcastReceiver所设置的过滤条件。 清楚这些成员变量和数据类型之间的关系后,接着来分析registerReceiver第二阶段的工作。 (2) registerReceiver分析之二 **ActivityManagerService.java::registerReceiver** ~~~ //如果allSticky不为空,则表示有Sticky的Intent,需要立即调度广播发送 if(allSticky != null) { ArrayList receivers = new ArrayList(); receivers.add(bf); intN = allSticky.size(); for(int i=0; i<N; i++) { Intent intent = (Intent)allSticky.get(i); //为每一个需要发送的广播创建一个BroadcastRecord(暂称之为广播记录)对象 BroadcastRecord r = new BroadcastRecord(intent, null, null, -1, -1, null,receivers, null, 0, null, null, false, true, true); //如果mParallelBroadcasts当前没有成员,则需要触发AMS发送广播 if (mParallelBroadcasts.size() == 0) scheduleBroadcastsLocked();//向AMS发送BROADCAST_INTENT_MSG消息 //所有非ordered广播记录都保存在mParallelBroadcasts中 mParallelBroadcasts.add(r); }//for循环结束 }//if (allSticky != null)判断结束 returnsticky; }//synchronized结束 } ~~~ 这一阶段的工作用一句话就能说清楚:为每一个满足IntentFilter的Sticky的intent创建一个BroadcastRecord对象,并将其保存到mParllelBroadcasts数组中,最后,根据情况调度AMS发送广播。 从上边的描述中可以看出,一旦存在满足条件的Sticky的Intent,系统需要尽快调度广播发送。说到这里,想和读者分享一种在实际工作中碰到的情况。 我们注册了一个BroadcastReceiver,用于接收USB的连接状态。在注册完后,它的onReceiver函数很快就会被调用。当时笔者的一些同事认为是注册操作触发USB模块又发送了一次广播,却又感到有些困惑,USB模块应该根据USB的状态变化去触发广播发送,而不应理会广播接收者的注册操作,这到底是怎么一回事呢? 相信读者现在应该轻松解决这种困惑了吧?对于Sticky的广播,一旦有接收者注册,系统会马上将该广播传递给它们。 和ProcessRecord及ActivityRecord类似,AMS定义了一个BroadcastRecord数据结构,用于存储和广播相关的信息,同时还有两个成员变量,它们作用和关系如图6-19所示。 :-: ![](http://img.blog.csdn.net/20150803123359357?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-19 BroadcastReceiver及相关变量 图6-19比较简单,读者可自行研究。 在代码中,registerReceiver将调用scheduleBroadcastsLocked函数,通知AMS立即着手开展广播发送工作,在其内部就发送BROADCAST_INTENT_MSG消息给AMS。相关的处理工作将放到本节最后再来讨论。