Android广播分为两个方面:广播发送者和广播接收者,一般状况下,BroadcastReceiver指的就是广播接收者(广播接收器)。广播做为Android组件间的通讯方式,可使用的场景以下:
1.同一app内部的同一组件内的消息通讯(单个或多个线程之间); html
2.同一app内部的不一样组件之间的消息通讯(单个进程); android
3.同一app具备多个进程的不一样组件之间的消息通讯; api
4.不一样app之间的组件之间消息通讯; 安全
5.Android系统在特定状况下与App之间的消息通讯。 网络
从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。所以,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统可以方便集成,更易扩展。具体实现流程要点粗略归纳以下: 并发
1.广播接收者BroadcastReceiver经过Binder机制向AMS(Activity Manager Service)进行注册; app
2.广播发送者经过binder机制向AMS发送广播; 异步
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(通常状况下是Activity)相应的消息循环队列中; ide
4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。 函数
对于不一样的广播类型,以及不一样的BroadcastReceiver注册方式,具体实现上会有不一样。但整体流程大体如上。
由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不肯定接收者究竟是什么时候才能接收到。显然,总体流程与EventBus很是相似。
在上文说列举的广播机制具体可使用的场景中,现分析实际应用中的适用性:
第一种情形:同一app内部的同一组件内的消息通讯(单个或多个线程之间),实际应用中确定是不会用到广播机制的(虽然能够用),不管是使用扩展变量做用域、基于接口的回调仍是Handler-post/Handler-Message等方式,均可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感受,会显太“重”;
第二种情形:同一app内部的不一样组件之间的消息通讯(单个进程),对于此类需求,在有些教复杂的状况下单纯的依靠基于接口的回调等方式很差处理,此时能够直接使用EventBus等,相对而言,EventBus因为是针对统一进程,用于处理此类需求很是适合,且轻松解耦。能够参见文件《Android各组件/控件间通讯利器之EventBus》。
第3、4、五情形:因为涉及不一样进程间的消息通讯,此时根据实际业务使用广播机制会显得很是适宜。下面主要针对Android广播中的具体知识点进行总结。
2.BroadcastReceiver
自定义BroadcastReceiver
自定义广播接收器须要继承基类BroadcastReceivre,并实现抽象方法onReceive(context, intent)方法。广播接收器接收到相应广播后,会自动回到onReceive(..)方法。默认状况下,广播接收器也是运行在UI线程,所以,onReceive方法中不能执行太耗时的操做。不然将所以ANR。通常状况下,根据实际业务需求,onReceive方法中都会涉及到与其余组件之间的交互,如发送Notification、启动service等。
下面代码片断是一个简单的广播接收器的自定义:
1 public class MyBroadcastReceiver extends BroadcastReceiver { 2 public static final String TAG = "MyBroadcastReceiver"; 3 public static int m = 1; 4 5 @Override 6 public void onReceive(Context context, Intent intent) { 7 Log.w(TAG, "intent:" + intent); 8 String name = intent.getStringExtra("name"); 9 Log.w(TAG, "name:" + name + " m=" + m); 10 m++; 11 12 Bundle bundle = intent.getExtras(); 13 14 } 15 }
BroadcastReceiver注册类型
BroadcastReceiver整体上能够分为两种注册类型:静态注册和动态注册。
1).静态注册:
直接在AndroidManifest.xml文件中进行注册。规则以下:
<receiver android:enabled=["true" | "false"] android:exported=["true" | "false"] android:icon="drawable resource" android:label="string resource" android:name="string" android:permission="string" android:process="string" > . . . </receiver>
其中,须要注意的属性
android:exported ——此broadcastReceiver可否接收其余App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,若是有intent-filter,默认值为true,不然为false。(一样的,activity/service中的此属性默认值同样遵循此规则)同时,须要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);
android:name —— 此broadcastReceiver类名;
android:permission ——若是设置,具备相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;
android:process ——broadcastReceiver运行所处的进程。默认为app的进程。能够指定独立的进程(Android四大基本组件均可以经过此属性指定本身的独立进程)
常见的注册形式有:
<receiver android:name=".MyBroadcastReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
其中,intent-filter因为指定此广播接收器将用于接收特定的广播类型。本示例中给出的是用于接收网络状态改变或开启启动时系统自身所发出的广播。当此App首次启动时,系统会自动实例化MyBroadcastReceiver,并注册到系统中。
以前常说:静态注册的广播接收器即便app已经退出,主要有相应的广播发出,依然能够接收到,但此种描述自Android 3.1开始有可能再也不成立,具体分析详见本文后面部分。
2).动态注册:
动态注册时,无须在AndroidManifest中注册<receiver/>组件。直接在代码中经过调用Context的registerReceiver函数,能够在程序中动态注册BroadcastReceiver。registerReceiver的定义形式以下:
1 registerReceiver(BroadcastReceiver receiver, IntentFilter filter) 2 registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
典型的写法示例以下:
1 public class MainActivity extends Activity { 2 public static final String BROADCAST_ACTION = "com.example.corn"; 3 private BroadcastReceiver mBroadcastReceiver; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 10 mBroadcastReceiver = new MyBroadcastReceiver(); 11 IntentFilter intentFilter = new IntentFilter(); 12 intentFilter.addAction(BROADCAST_ACTION); 13 registerReceiver(mBroadcastReceiver, intentFilter); 14 } 15 16 @Override 17 protected void onDestroy() { 18 super.onDestroy(); 19 unregisterReceiver(mBroadcastReceiver); 20 } 21 22 }
注:Android中全部与观察者模式有关的设计中,一旦涉及到register,一定在相应的时机须要unregister。所以,上例在onDestroy()回到中须要unregisterReceiver(mBroadcastReceiver)。
当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中。当此Activity销毁时,动态注册的MyBroadcastReceiver将再也不接收到相应的广播。
3.广播发送及广播类型
常常说”发送广播“和”接收“,表面上看广播做为Android广播机制中的实体,实际上这一实体自己是并非以所谓的”广播“对象存在的,而是以”意图“(Intent)去表示。定义广播的定义过程,实际就是相应广播”意图“的定义过程,而后经过广播发送者将此”意图“发送出去。被相应的BroadcastReceiver接收后将会回调onReceive()函数。
下段代码片断显示的是一个普通广播的定义过程,并发送出去。其中setAction(..)对应于BroadcastReceiver中的intentFilter中的action。
1 Intent intent = new Intent(); 2 intent.setAction(BROADCAST_ACTION); 3 intent.putExtra("name", "qqyumidi"); 4 sendBroadcast(intent);
根据广播的发送方式,能够将其分为如下几种类型:
1.Normal Broadcast:普通广播
2.System Broadcast: 系统广播
3.Ordered broadcast:有序广播
4.Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,再也不推荐使用,相应的还有粘性有序广播,一样已经deprecated)
5.Local Broadcast:App应用内广播
下面分别总结下各类类型的发送方式及其特色。
1).Normal Broadcast:普通广播
此处将普通广播界定为:开发者本身定义的intent,以context.sendBroadcast_"AsUser"(intent, ...)形式。具体可使用的方法有:
sendBroadcast(intent)/sendBroadcast(intent, receiverPermission)/sendBroadcastAsUser(intent, userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。
普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。若是发送广播时有相应的权限要求,BroadCastReceiver若是想要接收此广播,也须要有相应的权限。
2).System Broadcast: 系统广播
Android系统中内置了多个系统广播,只要涉及到手机的基本操做,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每一个系统广播都具备特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。
3)Ordered broadcast:有序广播
有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照前后循序接收。有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, ...)。
对于有序广播,其主要特色总结以下:
1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照前后顺序接收的,前后顺序断定标准遵循为:将当前系统中全部有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具备相同的priority的动态广播和静态广播,动态广播会排在前面。
2>先接收的BroadcastReceiver能够对此有序广播进行截断,使后面的BroadcastReceiver再也不接收到此广播,也能够对广播进行修改,使后面的BroadcastReceiver接收到广播后解析获得错误的参数值。固然,通常状况下,不建议对有序广播进行此类操做,尤为是针对系统中的有序广播。
4)Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,再也不推荐使用,相应的还有粘性有序广播,一样已经deprecated)。
既然已经deprecated,此处再也不多作总结。
5)Local Broadcast:App应用内广播(此处的App应用以App应用进程为界)
由前文阐述可知,Android中的广播能够跨进程甚至跨App直接通讯,且注册是exported对于有intent-filter的状况下默认值是true,由此将可能出现安全隐患以下:
1.其余App可能会针对性的发出与当前App intent-filter相匹配的广播,由此致使当前App不断接收到广播并处理;
2.其余App能够注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
不管哪一种情形,这些安全隐患都确实是存在的。由此,最多见的增长安全性的方案是:
1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,都增长上相应的permission,用于权限验证;
3.发送广播时,指定特定广播接收器所在的包名,具体是经过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
App应用内广播能够理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能须要用到。同时,之因此使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优点体如今:
1.安全性更高;
2.更加高效。
为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与一般的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。
代码片断以下:
1 //registerReceiver(mBroadcastReceiver, intentFilter); 2 //注册应用内广播接收器 3 localBroadcastManager = LocalBroadcastManager.getInstance(this); 4 localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter); 5 6 //unregisterReceiver(mBroadcastReceiver); 7 //取消注册应用内广播接收器 8 localBroadcastManager.unregisterReceiver(mBroadcastReceiver); 9 10 Intent intent = new Intent(); 11 intent.setAction(BROADCAST_ACTION); 12 intent.putExtra("name", "qqyumidi"); 13 //sendBroadcast(intent); 14 //发送应用内广播 15 localBroadcastManager.sendBroadcast(intent);
4.不一样注册方式的广播接收器回调onReceive(context, intent)中的context具体类型
1).对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;
2).对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;
3).对于经过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。
注:对于LocalBroadcastManager方式发送的应用内广播,只能经过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其余方式动态注册的ContextReceiver是接收不到的)。
5.不一样Android API版本中广播机制相关API重要变迁
1).Android5.0/API level 21开始粘滞广播和有序粘滞广播过时,之后再也不建议使用;
2).”静态注册的广播接收器即便app已经退出,主要有相应的广播发出,依然能够接收到,但此种描述自Android 3.1开始有可能再也不成立“
Android 3.1开始系统在Intent与广播相关的flag增长了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。
FLAG_INCLUDE_STOPPED_PACKAGES:包含已经中止的包(中止:即包所在的进程已经退出)
FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经中止的包
主要缘由以下:
自Android3.1开始,系统自己则增长了对全部app当前是否处于运行状态的跟踪。在发送广播时,无论是什么广播类型,系统默认直接增长了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,致使即便是静态注册的广播接收器,对于其所在进程已经退出的app,一样没法接收到广播。
详情参加Android官方文档:http://developer.android.com/about/versions/android-3.1.html#launchcontrols
由此,对于系统广播,因为是系统内部直接发出,没法更改此intent flag值,所以,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,若是App进程已经退出,将不能接收到广播。
可是对于自定义的广播,能够经过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即便所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是从新新建的。
1 Intent intent = new Intent(); 2 intent.setAction(BROADCAST_ACTION); 3 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 4 intent.putExtra("name", "qqyumidi"); 5 sendBroadcast(intent);
注1:对于动态注册类型的BroadcastReceiver,因为此注册和取消注册实在其余组件(如Activity)中进行,所以,不受此改变影响。
注2:在3.1之前,相信很多app可能经过静态注册方式监听各类系统广播,以此进行一些业务上的处理(如即时app已经退出,仍然能接收到,能够启动service等..),3.1后,静态注册接受广播方式的改变,将直接致使此类方案再也不可行。因而,经过将Service与App自己设置成不一样的进程已经成为实现此类需求的可行替代方案。