BroadcastReceiver属于Android四大组件之一
可用于监听应用发出的广播消息,并做出相应
应用场景 :
- 不同组件之间通信(包括应用内 / 不同应用之间)
- 与 Android 系统在特定情况下的通信 如当电话呼入时、网络可用时
- 多线程通信
一、实现原理
使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。
-
广播接收者 通过 Binder机制在 AMS 注册
-
广播发送者 通过 Binder 机制向 AMS 发送广播
-
AMS 根据 广播发送者 要求,在已注册列表中,寻找合适的广播接收者
寻找依据:IntentFilter / Permission
-
AMS将广播发送到合适的广播接收者消息循环队列中;
-
广播接收者通过 消息循环 拿到此广播,并回调 onReceive()
特别注意:广播发送者 和 广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;
二、广播接受者实现
继承自BroadcastReceivre基类,复写抽象方法onReceive()方法
广播接收器接收到相应广播后,会自动回调onReceive()方法
一般情况下,onReceive方法会涉及与其他组件之间的交互,如发送Notification、启动service等
默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作,超过10秒将导致ANR
不要在广播接收器里面开子线程,广播接受器的声明周期很短,很可能广播接受器被销毁了线程还没执行完,正确的处理方法就是通过调用activity或者service处理业务
2.1静态注册
在AndroidManifest.xml里通过 <receive> 标签声明
常用属性:
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
//这里的priority取值是-1000到1000,值越大优先级越高
<intent-filter android:priority = "777">
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
2.2动态注册
在代码中通过调用
@Override
protected void onResume() {
super.onResume();
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
registerReceiver(mBroadcastReceiver, intentFilter);
}
//注册广播后,要在相应位置记得销毁广播
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mBroadcastReceiver);
}
对于动态广播,有注册就必然得有注销,否则会导致内存泄露
重复注册、重复注销也不允许
2.3静态注册和动态注册的区别
1、静态注册的广播接收者一经安装就常驻在系统之中,不需要重新启动唤醒接收者;动态注册的广播接收者随着应用的生命周期,由registerReceiver开始监听,由unregisterReceiver撤销监听,如果应用退出后,没有撤销已经注册的接收者应用应用将会报错。
2、当广播接收者通过intent启动一个activity或者service时,如果intent中无法匹配到相应的组件。动态注册的广播接收者将会导致应用报错 而静态注册的广播接收者将不会有任何报错,因为自从应用安装完成后,广播接收者跟应用已经脱离了关系。
三、广播类型
3.1普通广播
应用在需要通知各个广播接收者的情况下使用,如 开机启动
使用方法:sendBroadcast()
Intent intent = new Intent("android.provider.Telephony.SMS_RECEIVED");
//通过intent传递少量数据
intent.putExtra("data", "finch");
// 发送普通广播
sendBroadcast(Intent);
普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,所有满足条件的 BroadcastReceiver 都会执行其 onReceive() 方法。
消息传递的效率比较高,并且无法中断广播的传播。
3.2有序广播
应用在需要有特定拦截的场景下使用,如黑名单短信、电话拦截。
使用方法:sendOrderedBroadcast(intent, receiverPermission);
receiverPermission :一个接收器必须持有对应的权限才能接收你的广播。如果为 null ,不加限制(一般都为null)。
在有序广播中,我们可以在前一个广播接收者将处理好的数据传送给后面的广播接收者,也可以调用abortBroadcast()来终结广播的传播
public void onReceive(Context arg0, Intent intent) {
//获取上一个广播的bundle数据
Bundle bundle = getResultExtras(true);//true:前一个广播没有结果时创建新的Bundle;false:不创建Bundle
bundle.putString("key", "777");
//将bundle数据放入广播中传给下一个广播接收者
setResultExtras(bundle);
//终止广播传给下一个广播接收者
abortBroadcast();
}
按优先级接受广播
3.3异步广播
使用方法:sendStickyBroadcast()
发出的Intent当接收Activity(动态注册)重新处于onResume状态之后就能重新接受到其Intent
就是说sendStickyBroadcast发出的最后一个Intent会被保留,下次当Activity处于活跃的时候又会接受到它。
发这个广播需要权限:
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
卸载该广播:removeStickyBroadcast(intent);
在卸载之前该intent会保留,接收者在可接收状态都能获得。
3.4异步有序广播
使用方法:sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras):
这个方法具有有序广播的特性也有异步广播的特性;同时需要权限:
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
3.5系统广播
Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
系统操作 | action |
监听网络变化 | |
关闭或打开飞行模式 | Intent.ACTION_AIRPLANE_MODE_CHANGED |
充电时或电量发生变化 | Intent.ACTION_BATTERY_CHANGED |
电池电量低 | Intent.ACTION_BATTERY_LOW |
电池电量充足(即从电量低变化到饱满时会发出广播) | Intent.ACTION_BATTERY_OKAY |
系统启动完成后(仅广播一次) | Intent.ACTION_BOOT_COMPLETED |
按下照相时的拍照按键(硬件按键)时 | Intent.ACTION_CAMERA_BUTTON |
屏幕锁屏 | Intent.ACTION_CLOSE_SYSTEM_DIALOGS |
设备当前设置被改变时(界面语言、设备方向等) | Intent.ACTION_CONFIGURATION_CHANGED |
插入耳机时 | Intent.ACTION_HEADSET_PLUG |
未正确移除SD卡但已取出来时(正确移除方法:设置--SD卡和设备内存--卸载SD卡) | Intent.ACTION_MEDIA_BAD_REMOVAL |
插入外部储存装置(如SD卡) | Intent.ACTION_MEDIA_CHECKING |
成功安装APK | Intent.ACTION_PACKAGE_ADDED |
成功删除APK | Intent.ACTION_PACKAGE_REMOVED |
重启设备 | Intent.ACTION_REBOOT |
屏幕被关闭 | Intent.ACTION_SCREEN_OFF |
屏幕被打开 | Intent.ACTION_SCREEN_ON |
关闭系统时 | Intent.ACTION_SHUTDOWN |
重启设备 | Intent.ACTION_REBOOT |
3.6本地广播
Android中的广播默认可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
可能出现的问题:
- 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
- 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
解决方案 使用App应用内广播(Local Broadcast)
App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高
具体使用1 - 将全局广播设置成局部广播
-
注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
-
在广播发送和接收时,增设相应权限permission,用于权限验证;
-
发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
通过 intent.setPackage(packageName) 指定包名
具体使用2 - 使用封装好的LocalBroadcastManager类 使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例
注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册
//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver
mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//步骤2:实例化LocalBroadcastManager的实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//步骤3:设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);
3.7 粘性广播
由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结
四、总结
静态广播接收的处理器是由PackageManagerService负责,当手机启动或者新安装了应用的时候,PackageManagerService会扫描手机中所有已安装的APP应用,将AndroidManifest.xml中有关注册广播的信息解析出来,存储至一个全局静态变量当中。
动态广播接收的处理器是由ActivityManagerService负责,当APP的服务或者进程起来之后,执行了注册广播接收的代码逻辑,即进行加载,最后会存储在一个另外的全局静态变量中。需要注意的是:这个并非是一成不变的,当程序被杀死之后,已注册的动态广播接收器也会被移出全局变量,直到下次程序启动,再进行动态广播的注册,当然这里面的顺序也已经变更了一次。
广播发出的时候,广播接收者接收的顺序如下:
1.当广播为普通广播时,有如下的接收顺序:
无视优先级 动态优先于静态
同优先级的动态广播接收器,先注册的大于后注册的
同优先级的静态广播接收器,先扫描的大于后扫描的
2.如果广播为有序广播,那么会将动态广播处理器和静态广播处理器合并在一起处理广播的消息,最终确定广播接收的顺序:
优先级高的先接收
同优先级的动静态广播接收器,动态优先于静态
同优先级的动态广播接收器,先注册的大于后注册的
同优先级的静态广播接收器,先扫描的大于后扫描的