一、定义&作用、分类、使用场景
1.定义
Broadcast在Android系统中应用的非常广泛,BroadcastReceiver是四大组件之一,Android中我们要发送的广播内容是一个Intent,这个Intent中可以携带我们要传送的数据。Broadcast可以实现不同程序间的数据传输与共享,只要和发送广播action相同的广播接收者,都可以接收到这个广播,也就是说,发送一个广播可以被很多广播接收者接收,也就是说BroadCastReceiver可以通过监听其它应用程序发送的广播接收传递过来的信息进而实现进程间的通信
2.分类
2.1 从发送方式来说:
Normal Broadcast(普通广播):完全异步的,可以在同一时刻被所有接收者接收到,消息传递的效率比较高,并且无法中断广播的传播,通常调用sendBroadcast(Intent)(Intent, String)方法发送
System Broadcast(系统广播):发生各种事件时,系统自动发送
Ordered Broadcast(有序广播):发送有序广播后,广播接收者将按预先声明的优先级依次接收Broadcast.优先级高的先接收到广播,而在其onRecevier()执行过程中,广播不会传播到下一个接收者,此时当前的广播接收者可以abortBroadcast()来阻止广播继续向下传播。调用sendOrderedBroadcast(Intent, String)方法发送
'Sticky Broadcast'(粘性广播):sendStickyBroadcast()来发送该类型的广播消息,当粘性广播发送后,最后一个粘性广播会滞留在操作系统中,在粘性广播发送后的一段时间里,如果有新的符合广播的动态注册广播接收者,将会收到这个广播消息。对于静态注册的广播接收者来说,这个等同于普通广播。已弃用(API 21)
LocalBroadcastManager本地广播:只在自身App内传播,由LocalBroadcastManager完成
2.2 注册方式
广播的注册方式来分,分为以下2种:
a.静态注册:通过
b.动态注册:通过context. registerReceiver在程序中显示注册的广播;
2.3隐式/显示
与应用程序无直接关系的任何广播都是隐式广播,比如ACTION_PACKAGE_REPLACE是一个隐式广播,因为他会通知手机上每个安装的包。
同样,与您的应用程序直接相关的任何广播都是显示广播
3.场景
a..app全局监听:不同APP之间的组件之间的消息通信。在AndroidManifest中静态注册的广播接收器,一般我们在收到该消息后,需要做一些相应的动作,而这些动作与当前App的组件,比如Activity或者Service的是否运行无关。
b.不同app之间的组件之间消息通信。
比如:Activity或者Service中使用registerReceiver()动态注册的广播接收器。比如网络连接发生变化时,需要在当前Activity页面给用户一些UI 上的提示,或者将Service中的网络请求任务暂停。
二、使用方式
1.注册广播
静态注册:通过在AndroidManifest清单文件中用
动态注册:跟随Activity的生命周期,是在代码中调用registerReceiver来进行注册的,会随着Actvity的销毁而销毁。
IntentFilter _Filter = new IntentFilter();
_Filter.setPriority(0x7fffffff);
//系统广播: 开屏
_Filter.addAction(Intent.ACTION_USER_PRESENT);
//系统广播: 开机
_Filter.addAction(Intent.ACTION_BOOT_COMPLETED);
//系统广播: USB插拔
_Filter.addAction(Intent.ACTION_POWER_CONNECTED);
_Filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
//系统广播: 电池信息变化
_Filter.addAction(Intent.ACTION_BATTERY_CHANGED);
//系统广播: 网络切换
_Filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.getApplicationContext().registerReceiver(mTestBroadcastReceiver, _Filter);
2.LocalBroadcastManager
LocalBroadcastManager是android support v4包里提供的一个组件,用来在应用内发送广播。
使用方法和和Broadcast基本一致,只需要在Broadcast相关的代码前加上LocalBroadcastManager.getInstance(context)就行了。
发送广播:
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
注册广播监听器:
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter);
注销广播监听器:
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);
相比于发送全局广播的sendBroadcast有很多优点:
(1)广播的数据不会离开本身的进程,所以不用担心泄露私人数据;
(2)其他应用程序不可能发广播给你的应用,所以不用担心有安全漏洞会被利用;
(3)相比于经过系统的全局广播更有效率。
与Handler进行比较:
通过线程内的通信用Handler会更方便,所以这种LocalBroadcastManager也是比较少用,不过相比于Handler,LocalBroadcastManager的优势在于如果在通过线程内,多个对象要收到消息,LocalBroadcastReceiver发一次,而Handler则要发多次。
三、实现机制原理
1.设计模式
Android中的广播使用了观察者模式,模型为基于消息的发布/订阅事件模型。
2.模型成员:
消息发布者(广播发布者)
消息订阅者(广播接收者)
消息中心(AMS,Activity Manager Service,一个Android系统中极其重要!的成分,以后我们会详细讲解)
发布订阅模式属于广义上的观察者模式
前者时最常用的一种观察者模式的实现,且从解耦和重用角度上看更优于典型的观察者模式
发布订阅模式加入消息中心,实现发布者和订阅者的解耦:
在观察者模式中,观察者需要直接订阅目标事件,在目标发出内容改变的事件后,直接接收事件并作出响应。
在发布订阅模式中,多了一个消息中心,一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要从消息中心订阅事件。以此避免发布者和订阅者之间产生依赖关系。
3.实现流程
广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
广播发送者通过binder机制向AMS发送广播;
AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver
AMS将广播发送到上述符合条件的BroadcastReceiver相应的消息循环队列中
BroadcastReceiver通过消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。
四、补充
1.BroadcastReceiver的生命周期
1.动态注册:存活周期是在Context.registerReceiver和Context.unregisterReceiver之间,BroadcastReceiver每次收到广播都是使用注册传入的对象处理的。
2.静态注册:进程在的情况下,receiver会正常收到广播,调用onReceive方法,生命周期只存活在onReveive函数中,此方法结束,BroadcastReceiver就销毁了。进程不存在时,广播相应的进程会被激活,Application.onCreate会被调用,再调用onReceive()
2.unregisterReceiver
Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister,防止内存泄漏。因此,上例在onDestroy()回调需要unregisterReceiver(mBroadcastReceiver)。
3Android 7.0 更改
Android 7.0起,系统不再发送以下系统广播:
ACTION_NEW_PICTURE
ACTION_NEW_VIDEO
针对Android 7.0 (API级别24)和更高版本的应用程序必须通过registerReceiver()注册以下广播。在AndroidManifest中声明
CONNECTIVITY_ACTION
4.Android 8.0
8.0起,应用无法在Manifest中注册大部分隐式系统广播(即,并非专门针对此应用的广播),此意也是在于降低随Android同时运行的应用增多,发生性能变差的几率。
解决:
优先使用动态注册Receiver的方式,能动态注册绝不使用Manifest注册
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.xxx.receiver");
TheReceiver receiver = new TheReceiver();
registerReceiver(receiver, intentFilter);
如果一定要Manifest注册,那么当发送广播的时候,指定广播接收者的包名,即发送显式广播
如果我们不想发显式广播(因为我们不知道有谁要收广播),对方又不能动态注册,只能静态注册(许多应用希望是被动唤醒),我们应该怎么办呢?
解决办法:
发送广播的时候携带intent.addFlags(0x01000000); 即能让广播突破隐式广播限制。
参见:https://www.jianshu.com/p/5283ebc225d5