name="android.intent.action.BOOT_COMPLETED"/>
其中:
android:
enabled表示此broadcastReceiver是否可用,默认值为true。
android:
exported表示此broadcastReceiver能否接收其它App的发出的广播;
如果标签中定义了intent-filter字段,则此值默认值为true,否则为false。
android:
name表示BroadcastReceiver的类名。
android:
permission用于指定广播发送方应该具有的权限;
即具有对应权限的发送者,才能通过AMS将广播发送给此broadcastReceiver;
android:
process表示broadcastReceiver运行的进程;
BroadcastReceiver默认运行在当前app的进程中,也可以通过此字段指定其运行于其它独立的进程。
intent-filter用于指定该broadcastReceiver接收广播的类型。
动态注册
与静态注册不同,动态注册是指:
应用程序在运行过程中,调用Context的registerReceiver函数注册BroadcastReceiver;
当应用程序不再需要监听广播时,则需要调用unregisterReceiver函数进行反注册。
动态注册BroadcastReceiver时,需要指定对应的IntentFilter。
IntentFilter用于描述该BroadcastReceiver期待接受的广播类型。
同时,动态注册时也可以指定该BroadcastReceiver要求的权限。
2广播的种类
从广播的发送特点来看,可以将Android中定义的广播分为以下几类:
普通广播
普通广播由发送方调用sendBroadcast及相关重载函数发送。
AMS转发这种类型的广播时,根据BroadcastReceiver的注册方式,进行不同的处理流程。
对于动态注册的广播,理论上可以认为,AMS将在同一时刻,向所有监听此广播的BroadcastReceiver发送消息,因此整体的消息传递的效率比较高。
对于静态注册的广播,AMS将按照有序广播的方式,向BroadcastReceiver发送消息。
有序广播
有序广播由发送方调用sendOrderedBroadcast及相关重载函数发送,是一种串行的广播发送方式。
处理这种类型的广播时,AMS会按照优先级,将广播依次分发给BroadcastReceiver。
AMS收到上一个BroadcastReceiver处理完毕的消息后,才会将广播发送给下一个BroadcastReceiver。
其中,任意一个BroadcastReceiver,都可以中止后续的广播分发流程。
同时,上一个BroadcastReceiver可以将额外的信息添加到广播中。
前文已经指出,当普通广播发送给静态注册的BroadcastReceiver时,AMS实际上是按照有序广播的方式来进行发送,这么做的原因是:
静态注册的BroadcastReceiver仅申明在AndroidManifest.xml中,因此其所在的进程可能并没有启动。
当AMS向这些BroadcastReceiver发送消息时,可能必须先启动对应的进程。
如果同时向静态注册的BroadcastReceiver发送广播,那么可能需要在一段时间内同时创建出大量的进程,
这将对系统造成极大的负担。
若以有序广播的方式来发送,那么系统可以依次创建进程,
同时,每次收到上一个广播处理完毕的消息后,都可以尝试清理掉无用的进程。
这样即可以避免突发创建大量的进程,又可以及时回收一些系统资源。
因此从Android的设计方式可以看出,从接收广播的效率来讲:
排在第一的是,接收普通广播的动态注册的BroadcastReceiver,
其次是,接收有序广播的动态注册的BroadcastReceiver;
最后是,静态注册的BroadcastReceiver,此时接收的广播是否有序,已经不重要了。
实际上,源码就是按这种顺序来处理的,我们后面将进行深入分析。
粘性广播
粘性广播由发送方调用sendStickyBroadcast及相关重载函数发送。
需要注意的是,目前这种广播已经被附上Deprecated标签了,不再建议使用。
Android设计这种广播的初衷是:
正常情况下,假设发送方发送了一个普通广播A。
在广播A发送完毕后,系统中新注册了一个BroadcastReceiver,此时这个BroadcastReceiver是无法收到广播A的。
但是,如果发送方发送的是一个粘性广播B,那么系统将负责存储该粘性广播。
于是,即使BroadcastReceiver在广播B发送完后才注册到系统,这个BroadcastReceiver也会立即收到AMS转发的广播B。
粘性广播和有序广播等概念实际上不是冲突的。
粘性仅仅强调系统将会保存这个广播,它的其它处理过程与上文一致。
3适时地限制广播发送范围
从上文可知,对于定义了intent-filter的BroadcastReceiver而言,其exported属性默认为true。
这种设计是符合预期的,毕竟我们使用广播的目的,就是使得属于不同应用、不同进程的组件能够彼此通信。
但在某些场景下,这种设计也可能带来一些安全隐患:
1.其它App可能会针对性的发出与当前Appintent-filter相匹配的广播,导致当前App不断接收到广播并处理;
2.其它App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
因此,在必要的时候,需要适时地限制广播发送范围,例如:
1.对于同一App内部发送和接收广播,将exported属性设置成false;
2.在广播发送和接收时,都增加上相应的permission;
3.发送广播时,指定BroadcastReceiver对应的包名。
通过以上三种方式,可以缩小广播的发送和接收范围,提高效率和安全性。
实际上,在framework/support/v4/java/android/support/v4/content目录下,Android定义了LocalBroadcastManager类,用于发送和接收仅在同一个App应用内传递的广播。
这个类仅针对动态注册的BroadcastReceiver有效,具体的使用方式与普通的全局广播类似,
只是注册/反注册BroadcastReceiver和发送广播时,将Context变成了LocalBroadcastManager。
举例如下:
//registerReceiver
localBroadcastManager=LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver,intentFilter);
..........
//unregisterReceiver
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
..........
//sendbroadcast
Intentintent=newIntent();
intent.setAction("Test");
localBroadcastManager.sendBroadcast(intent);
...........
以上是Android中关于广播的基础知识,接下来进入到实际的源码分析。
二、registerReceiver流程分析
现在我们看一下广播相关的源码,先从BroadcastReceiver的动态注册流程开始分析。
1ContextImpl中的registerReceiver
动态注册BroadcastReceiver,将使用Context中定义的registerReceiver函数,具体的实现定义于ContextImpl中:
//这是最常用的接口
publicIntentregisterReceiver(BroadcastReceiverreceiver,IntentFilterfilter){
returnregisterReceiver(receiver,filter,null,null);
}
//broadcastPermission与静态注册中的permission标签对应,用于对广播发送方的权限进行限制
//只有拥有对应权限的发送方,发送的广播才能被此receiver接收
//不指定scheduler时,receiver收到广播后,将在主线程调用onReceive函数
//指定scheduler后,onReceive函数由scheduler对应线程处理
publicIntentregisterReceiver(BroadcastReceiverreceiver,IntentFilterfilter,
StringbroadcastPermission,Handlerscheduler){
//ContextImpl是Context家族中实际工作的对象,getOuterContext得到的是ContextImpl对外的代理
//一般为Application、Activity、Service等
returnregisterReceiverInternal(receiver,getUserId(),
filter,broadcastPermission,scheduler,getOuterContext());
}
与上述代码类似,不论调用Context中的哪个接口注册BroadcastReceiver,最终流程均会进入到registerReceiverInternal函数。
现在,我们跟进一下registerReceiverInternal函数:
privateIntentregisterReceiverInternal(BroadcastReceiverreceiver,intuserId,
IntentFilterfilter,StringbroadcastPermission,
Handlerscheduler,Contextcontext){
IIntentReceiverrd=null;
if(receiver!
=null){
//mPackageInfo的类型为LoadedApk
if(mPackageInfo!
=null&&context!
=null){
if(scheduler==null){
//未设置scheduler时,将使用主线程的handler处理
//这就是默认情况下,Receiver的onReceive函数在主线程被调用的原因
scheduler=mMainThread.getHandler();
}
//getReceiverDispatcher函数的内部,利用BroadcastReceiver构造出ReceiverDispatcher
//返回ReceiverDispatcher中的IIntentReceiver对象
//IIntentReceiver是注册到AMS中,供AMS回调的Binder通信接口
rd=mPackageInfo.getReceiverDispatcher(
receiver,context,scheduler,
mMainThread.getInstrumentation(),true);
}else{
//这段代码写的我瞬间懵逼了。
。
。
scheduler==null对应的判断,明显可以移出if-else结构的
//不符合大google的逼格
if(scheduler==null){
scheduler=mMainThread.getHandler();
}
//主动创建
rd=newLoadedApk.ReceiverDispatcher(
receiver,context,scheduler,null,true).getIIntentReceiver();
}
}
try{
//将Receiver注册到AMS
finalIntentintent=ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(),mBasePackageName,
rd,filter,broadcastPermission,userId);
.................
}catch(RemoteExceptione){
...........
}
}
以上代码中主要的工作是:
创建BroadcastReceiver对应的ReceiverDispatcher,得到ReceiverDispatcher内部的IIntentReceiver对象,
然后利用Binder通信将该对象连同BroadcastReceiver的其它信息注册到AMS中。
IIntentReceiver对应的继承关系如下图所示:
结合上图,我们需要知道的是:
1、BroadcastReceiver收到的广播实际上是AMS发送的,因此BroadcastReceiver与AMS之间必须进行Binder通信。
从类图可以看出,BroadcastReceiver及其内部成员并没有直接继承Binder类。
负责与AMS通信的,实际上是定义于LoadedApk中的InnerReceiver类,该类继承IIntentReceiver.Stub,作为Binder通信的服务端。
从上文的代码可以看出,注册BroadcastReceiver时,会将该对象注册到AMS中供其回调。
当InnerReceiver收到AMS的通知后,将会调用ReceiverDispatcher进行处理。
由于ReceiverDispatcher持有了实际的BroadcastReceiver,于是最终将广播递交给BroadcastReceiver的onReceive函数进行处理。
以上的设计思路实际上是:
将业务接口(处理广播的BroadcastReceiver类)与通信接口(InnerReceiver类)分离,由统一的管理类(ReceiverDispatcher)进行衔接。
这基本是Java层使用Binder通信的标配,AIDL本身也是这种思路。
2、BroadcastRecevier中定义了一个PendingResult类,该类用于异步处理广播消息。
从上文的代码我们知道了,如果没有明确指定BroadcastReceiver对应的handler,那么其onReceive函数将在主线程中被调用。
因此当onReceive中需要执行较耗时的操作时,会阻塞主线程,影响用户体验。
为了解决这种问题,可以在onReceive函数中采用异步的方式处理广播消息。
一提到异步的方式处理广播消息,大家可能会想到在BroadcastReceiver的onReceive中单独创建线程来处理广播消息。
然而,这样做存在一些问题。
例如:
对于一个静态注册的BroadcastReceiver对象,对应的进程初始时可能并没有启动。
当AMS向这个BroadcastReceiver对象发送广播时,才会先启动对应的进程。
一旦BroadcastReceiver对象处理完广播,i并将返回结果通知给AMS后,AMS就有可能清理掉对应进程。
因此,若在onReceive中创建线程处理问题,那么onReceive函数就可能在线程完成工作前返回,导致AMS提前销毁进程。
此时,线程也会消亡,使得工作并没有有效完成。
为了解决这个问题,就可以用到BroadcastReceiver提供的PendingResult。
具体的做法是:
先调用BroadcastReceiver的goAsync函数得到一个PendingResult对象,
然后将该对象放到工作线程中去释放。
这样onReceive函数就可以立即返回而不至于阻塞主线程。
同时,Android系统将保证BroadcastReceiver对应进程的生命周期,
直到工作线程处理完广播消息后,调用PendingResult的finish函数为止。
其中原理是:
正常情况下,BroadcastReceiver在onReceive函数结束后,
判断PendingResult不为null,才会将处理完毕的信息通知给AMS。
一旦调用BroadcastReceiver的goAsync函数,就会将BroadcastReceiver中的PendingResult置为null,
因此即使onReceive函数返回,也不会将信息通知给AMS。
AMS也就不会处理BroadcastReceiver对应的进程。
待工作线程调用PendingResult的finish函数时,才会将处理完毕的信息通知给AMS。
代码示例如下:
privateclassMyBroadcastReceiverextendsBroadcastReceiver{
..................
publicvoidonReceive(finalContextcontext,finalIntentintent){
//得到PendingResult
finalPendingResultresult=goAsync();
//放到异步线程中执行
AsyncHandler.post(newRunnable(){
@Override
publicvoidrun(){
handleIntent(context,intent);//可进行一些耗时操作
result.finish();
}
});
}
}
finalclassAsyncHandler{
privatestaticfinalHandlerThreadsHandlerThread=newHandlerThread("AsyncHandler");
privatestaticfinalHandlersHandler;
static{
sHandlerThread.start();
sHandler=newHandler(sHandlerThread.getLooper());
}
publicstaticvoidpost(Runnabler){
sHandler.post(r);
}
privateAsyncHandler(){}
}
需要注意的是:
在onReceive函数中执行异步操作,主要目的是避免一些操作阻塞了主线程,
但整个操作仍然需要保证在10s内返回结果,尤其是处理有序广播和静态广播时。
毕竟AMS必须要收到返回结果后,才能向下一个BroadcastReceiver发送广播。
2AMS中的registerReceiver
AMS的registerReceiver函数被调用时,将会保存BroadcastReceiver对应的Binder通信端,我们一起来看看这部分的代码:
publicIntentregisterReceiver(IApplicationThreadcaller,StringcallerPackage,
IIntentReceiverreceiver,IntentFilterfilter,Stringpermission,intuserId){
...............
//保存系统内已有的sticky广播
ArrayListstickyIntents=null;
...............
synchronized(this){
if(caller!
=null){
//根据IApplicationThread得到对应的进程信息
callerApp=getRecordForAppLocked(caller);
if(callerApp==null){
//抛出异常,即不允许未登记的进程注册BroadcastReceiver
...............
}
if(callerApp.info.uid!
=Proc