在Android中,Broadcast是一种广泛应用在应用程序之间传输信息的机制,Android中我们要发送的内容是一个Intent,而我们要发送的数据由Intent包装。
而BroadcastReceiver是一个专注于接收广播发出的信息,并作出对应处理的组件。BroadcastReceiver没有用户界面,但是它可以启动Activity来相应接收到的消息,或者使用NotificationManager发出通知来提醒用户
广播类似于Java当中的观察者模式,即被观察者数据发生变化后,给观察者发出通知做相应的数据处理。
同一个APP具有多个进程的不同组件之间的消息通信
比如获取定位数据或者天气数据的Service放在了一个进程,这时候可以通过广播来与主进程进行数据交换达到更新UI等功能
不同APP之间的组件进行消息通信
比如系统应用发出来电信息,短信信息,电源信息等。或者一些大公司有多个APP装在用户手机上,这样不同应用之间就可以通过广播来进行通信
Normal Boradcast:即普通广播,可以通过Conext.sendBroadcast(intent)、Conext.sendBroadcast(intent,receiverPermission)等发送广播
System Broadcast:即系统广播,系统广播在系统内部当特定事件发生时,由系统自动发出,开发者可以接受这些广播;如:开机启动,网络状态改变,拍照,屏幕关闭与开启等特定事件
Ordered Broadcast:即有序广播,其实有序广播可以算做是系统广播;有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, …);有序广播特点如下:
Local Broadcast:即本地广播,App应用内广播,广播的发送与接收只在当前应用内发生
Intent intent = new Intent();
intent.setAction(BootReceiver.ACTION);
sendBroadcast(intent);
Intent intent = new Intent(this,BootReceiver.class);
intent.setAction(BootReceiver.ACTION);
sendBroadcast(intent);
这种自己定义的【android.intent.action.MANGO】是一个显示广播,而很多跟自己应用无关的action是隐式广播
对于系统发出的广播,基本上都是隐式广播,以前很多应用会在Manifest中为很多系统隐式广播注册接收器,然后通过一些操作来提高自己的应用进程优先级,也就是俗称的【进程保活】,但是如果大家都这样干,将会极大的消耗系统资源,手机也就变的越来越卡
Google也意识到这个问题了,于是在Android N这个版本做了一些后台优化,删除了三项隐式广播CONNECTIVITY_CHANGE,ACTION_NEW_PICTURE ,ACTION_NEW_VIDEO,以帮助优化内存使用和电量消耗;这样即使你静态注册了这些广播,也不会收到了;但是你如果是动态注册广播,还是可以收到这些广播的
在Android N平台下即使在Manifest.xml清单文件中注册了 CONNECTIVITY_ACTION广播,在网络发生变化时也不会接收到任何的信息。但是正在前台运行的应用程序如果在主线程中通过Context.registerReceiver()动态注册了CONNECTIVITY_ACTION广播,该应用程序仍然可以接收到该广播。(注:这样开发者就可以根据不同的网络状态加载相应的页面信息了,从而提高用户体验)。
应用程序无法发送或接收 ACTION_NEW_PICTURE(拍照) 或 ACTION_NEW_VIDEO(录像) 广播。此项优化会影响所有应用,而不仅仅是面向 Android N 的应用。
鉴于开发者们肯定更多的只是考虑自己的应用,你可以想象一下手机里的每个应用可能都会去监听网络状态变更、是否拍摄了新照片、安装了新应用、开始充电等等事件。因为这些 App 都在 AndroidManifest.xml 中注册接收这些广播,所以它们总是能被唤醒接收这些广播,即使不在前台,甚至没有运行。Google 也意识到隐式广播被滥用了,因此在 Android O 中清除了非常多的隐式广播。这样即使你静态注册了这些广播,也不会收到了;但是你如果是动态注册广播,还是可以收到这些广播的
但是有一些隐式广播还是可以收到的(我们自己正常发的广播,如果不指定包名, 静态注册的也是收不到的),如下:
ACTION_LOCKED_BOOT_COMPLETED, ACTION_BOOT_COMPLETED
豁免,因为这些广播第一次启动时只发送一次,许多应用程序需要接收这个广播来安排作业,报警等。
ACTION_USER_INITIALIZE, "android.intent.action.USER_ADDED", "android.intent.action.USER_REMOVED"
这些广播受到特权许可保护,所以大多数普通应用无法接收。
"android.intent.action.TIME_SET", ACTION_TIMEZONE_CHANGED, ACTION_NEXT_ALARM_CLOCK_CHANGED
当时间,时区或闹钟发生变化时,时钟应用可能需要接收这些广播来更新闹钟。
ACTION_LOCALE_CHANGED
只有当地区变化时才发送,这不常见。当区域设置更改时,应用程序可能需要更新其数据。
ACTION_USB_ACCESSORY_ATTACHED, ACTION_USB_ACCESSORY_DETACHED, ACTION_USB_DEVICE_ATTACHED, ACTION_USB_DEVICE_DETACHED
如果一个应用程序需要知道这些与USB有关的事件,那么目前还没有注册广播的好选择。
ACTION_HEADSET_PLUG
由于此广播仅在用户物理连接或断开插头时发送,因此如果应用响应此广播,则不太可能影响用户体验。
ACTION_CONNECTION_STATE_CHANGED, ACTION_CONNECTION_STATE_CHANGED, ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED
类似于ACTION_HEADSET_PLUG,如果应用程序接收到这些蓝牙事件的广播,用户体验就不会受到影响。
ACTION_CARRIER_CONFIG_CHANGED, TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED, "TelephonyIntents.SECRET_CODE_ACTION"
OEM电话应用可能需要接收这些广播。
LOGIN_ACCOUNTS_CHANGED_ACTION
某些应用程序需要了解登录帐户的更改,以便他们可以为新帐户和已更改帐户设置计划操作。
ACTION_PACKAGE_DATA_CLEARED
仅当用户从“设置”显式清除其数据时才发送,因此广播接收者不太可能显着影响用户体验。
ACTION_PACKAGE_FULLY_REMOVED
某些应用程序可能需要在删除另一个软件包时更新其存储的数据; 对于这些应用程序,没有注册此广播的好选择。
注意:其他与封装相关的广播(如ACTION_PACKAGE_REPLACED)不能免于新的限制。这些广播是非常普遍的,因为它们对于免除这些播放具有潜在的性能影响。
ACTION_NEW_OUTGOING_CALL
为响应用户拨打电话而采取行动的应用程序需要接收此广播。
ACTION_DEVICE_OWNER_CHANGED
这种广播不经常发送; 某些应用程序需要接收它,以便他们知道设备的安全状态已更改。
ACTION_EVENT_REMINDER
由日历提供商发送事件提醒日历应用程序。由于日历提供程序不知道日历应用程序是什么,因此该广播必须是隐式的。
ACTION_MEDIA_MOUNTED, ACTION_MEDIA_CHECKING, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_EJECT, ACTION_MEDIA_UNMOUNTABLE, ACTION_MEDIA_REMOVED, ACTION_MEDIA_BAD_REMOVAL
这些广播是由于用户与设备的物理交互(安装或删除存储卷)或作为启动初始化的一部分(可用卷被安装)的结果而发送,因此它们不是常见的情况,并且通常在用户的控制之下。
SMS_RECEIVED_ACTION, WAP_PUSH_RECEIVED_ACTION
这些广播由SMS收件人应用程序依赖。
静态注册方式,直接在Androidmanifest.xml中注册:
注册属性解释如下:
android:exported ——此broadcastReceiver能否接收其他App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);
ndroid:name —— 此broadcastReceiver类名;
android:permission ——如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;
android:process ——broadcastReceiver运行所处的进程。默认为app的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)
intent-filter ——用于指定此广播接收器接收特定的广播类型
可能有的同学还有这个印象,通过静态注册广播,即使应用退出了,广播接收器依然还在工作,收到相应广播后还会处理,但是Android 3.1开始,系统在Intent与广播相关的flag增加了参数,分别是
FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包
系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播
BroadcastDemo broadcastDemo = new BroadcastDemo();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com,mangoer.boradcast");
registerReceiver(broadcastDemo,intentFilter)
其中BoradcastDemo是我自定义的一个广播,给这个广播加了一个过滤器IntentFilter,表示这个广播接收器只接受这个类型的广播。注册之后一定要记得取消注册
if (broadcastDemo != null) {
unregisterReceiver(broadcastDemo);
}
我们知道BroadcastReceiver设计的初衷就是从全局考虑,方便应用程序和系统程序之间、应用程序与应用程序之间、应用程序内部的通信;但是对于单个应用程序使用BroadcastReceiver是存在安全风险的:
如果别人通过反编译你的程序,获取到你的广播接收者的intent-filter或者permission,那就可以:
解决方法如下:
private static LocalBroadcastManager mInstance;
public static LocalBroadcastManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
从这些代码可以总结到:
private final HashMap> mReceivers
= new HashMap>();
private final HashMap> mActions
= new HashMap>();
private final ArrayList mPendingBroadcasts
= new ArrayList();
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
synchronized (mReceivers) {
//构建广播接收器记录者,包含过滤条件和广播接收器
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
//构建保存过滤条件的集合
ArrayList filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList(1);
//将每个广播接收器和对应的过滤条件进行映射
mReceivers.put(receiver, filters);
}
//保存过滤条件
filters.add(filter);
for (int i=0; i entries = mActions.get(action);
if (entries == null) {
//将ArrayList容量设置为1,节约内存,不过容量还是会随着元素增加自增长
entries = new ArrayList(1);
//将action和广播接收器记录者 进行映射
mActions.put(action, entries);
}
//保存 广播接收器记录者
//注意这里entries会添加重复的entry
entries.add(entry);
}
}
}
这个注册方法主要就是将广播接收者和过滤条件保存在mReceivers和mActions中
public void unregisterReceiver(BroadcastReceiver receiver) {
synchronized (mReceivers) {
//从mReceivers集合移除该广播接收器,并取出对应的过滤条件集合
ArrayList filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
for (int i=0; i receivers = mActions.get(action);
if (receivers != null) {
for (int k=0; k
解除注册也就是把之前保存的数据清空
public boolean sendBroadcast(Intent intent) {
synchronized (mReceivers) {
//从intent中取出action
final String action = intent.getAction();
final String type = intent.resolveTypeIfNeeded(
mAppContext.getContentResolver());
final Uri data = intent.getData();
final String scheme = intent.getScheme();
final Set categories = intent.getCategories();
//从mActions集合中通过Intent中的action取出 广播接收器记录者 集合
ArrayList entries = mActions.get(intent.getAction());
if (entries != null) {
//保存要处理该Intent的广播接收器记录者
ArrayList receivers = null;
for (int i=0; i= 0) {
if (receivers == null) {
receivers = new ArrayList();
}
//如果能匹配到就将该 广播接收器记录者 保存到新的集合
receivers.add(receiver);
//表示 该广播接收器记录者 已经添加过了
receiver.broadcasting = true;
}
}
if (receivers != null) {
//将新的集合遍历,把broadcasting置否,以便于下次开发者继续发送广播
for (int i=0; i
发送广播逻辑:
private LocalBroadcastManager(Context context) {
mAppContext = context;
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
private void executePendingBroadcasts() {
while (true) {
BroadcastRecord[] brs = null;
synchronized (mReceivers) {
//没有需要处理的Intent,就return
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
//实例化BroadcastRecord数组,并且将mPendingBroadcasts中的数据添加到其中
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i=0; i
主要是通过构造方法中实例化的Handler来处理消息;每发送一次广播,就通过Handler发送一次消息处理,将Intent回调给对应的BroadcastRecevier的onReceive处理
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
BroadcastDemo broadcastDemo = new BroadcastDemo();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com,mangoer.boradcast");
broadcastManager.registerReceiver(broadcastDemo,intentFilter);
broadcastManager.unregisterReceiver(broadcastDemo);
BroadcastDemo broadcastDemo = new BroadcastDemo();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com,mangoer.boradcast");
Context.registerReceiver(broadcastDemo,intentFilter);
Context.unregisterReceiver(broadcastDemo);
sendBroadcast(Intent intent);
参数intent:携带Action标识信息(与注册广播是的IntentFilter中的action要相同)且可以携带数据
sendBroadcast(Intent intent, String receiverPermission)
参数intent:携带Action标识信息(与注册广播是的IntentFilter中的action要相同)且可以携带数据
参数receiverPermission:字面意思是指定广播接收者的权限,也就是说你的BroadcastReceiver注册的时候要添加了这个权限,后续才能收到这个广播