一 简介
1.1 定义
BroadcastReceiver即 广播,是一个全局的监听器,属于Android
四大组件之一
1.2 作用
监听 / 接收 应用 App
发出的广播消息,并 做出响应
1.3 应用场景
Android
不同组件间的通信(含 :应用内 / 不同应用之间)- 多线程通信
- 与
Android
系统在特定情况下的通信
二 实现原理
2.1 使用模型
Android
中的广播使用了设计模式中的观察者模式:基于消息的发布 / 订阅事件模型
2.2 模型讲解
-
模型中有3个角色:
- 消息订阅者(广播接收者)
- 消息发布者(广播发布者)
- 消息中心(
AMS
,即Activity Manager Service
)
-
示意图 & 原理如下
三 广播接收
3.1 静态注册
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
//注:Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
从8.0开始,系统对静态注册的广播会有所限制,无法使用清单为大多数隐式广播声明接收方。
倘若要继续使用,设定 package 和 class 即可,如下图:
3.2 动态注册
private void register() {
receiver = new MyActiveReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MyActiveReceiver.ACTION);
registerReceiver(receiver, filter);
}
private void unRegister() {
unregisterReceiver(receiver);
}
3.3 两种方式区别
四 广播类型
4.1 普通广播
private void sendStaticBroadcast() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(MyActiveReceiver.ACTION);
sendBroadcast(intent);
}
4.2 系统广播
- Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播
- 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
系统操作 | action |
---|---|
监听网络变化 | android.net.conn.CONNECTIVITY_CHANGE |
关闭或打开飞行模式 | 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 |
注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播
示例:
private void register() {
receiver = new MySystemReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);//亮屏广播
registerReceiver(receiver, filter);
}
private void unRegister() {
unregisterReceiver(receiver);
}
public class MySystemReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(Intent.ACTION_SCREEN_ON, action)) {
Toast.makeText(context, "收到亮屏广播", Toast.LENGTH_LONG).show();
}
}
}
4.3 有序广播
发送出去的广播被广播接收者按照先后顺序接收。
- 顺序规则(同时面向静态和动态注册的广播接受者)
1 按照Priority属性值从大-小排序;
2 Priority属性相同者,动态注册的广播优先;
- 特点
1 接收广播按顺序接收
2 先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
3 先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播
示例:
/**
* 有序广播
*/
public class MyOrder1Receiver extends BroadcastReceiver {
public static final String ACTION = "com.bsoft.ck.action.orderdemo";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(ACTION,action)){
Log.i("chenkaid","收到有序广播 1");
//传值
Bundle twobundle = new Bundle();
twobundle.putString("extra","来自Order1");
setResultExtras(twobundle);
}
}
}
/**
* 有序广播
*/
public class MyOrder2Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(MyOrder1Receiver.ACTION, action)) {
Bundle bundle = getResultExtras(true);
String extra = null;
if (bundle != null) {
extra = bundle.getString("extra");
}
Log.i("chenkaid", "收到有序广播 2 " + extra);
//拦截
abortBroadcast();
}
}
}
/**
* 有序广播
*/
public class MyOrder3Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(MyOrder1Receiver.ACTION,action)){
Log.i("chenkaid","收到有序广播 3");
}
}
}
private void sendStaticBroadcast() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(MyOrder1Receiver.ACTION);
sendOrderedBroadcast(intent, null);
}
private void register() {
myOrder1Receiver = new MyOrder1Receiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MyOrder1Receiver.ACTION);
filter.setPriority(1);
registerReceiver(myOrder1Receiver, filter);
myOrder2Receiver = new MyOrder2Receiver();
IntentFilter filter2 = new IntentFilter();
filter2.addAction(MyOrder1Receiver.ACTION);
filter2.setPriority(2);
registerReceiver(myOrder2Receiver, filter);
myOrder3Receiver = new MyOrder3Receiver();
IntentFilter filter3 = new IntentFilter();
filter3.addAction(MyOrder1Receiver.ACTION);
filter3.setPriority(3);
registerReceiver(myOrder3Receiver, filter);
}
private void unRegister() {
unregisterReceiver(myOrder1Receiver);
unregisterReceiver(myOrder2Receiver);
unregisterReceiver(myOrder3Receiver);
}
}
4.4 应用内广播
- 背景
Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
- 可能出现的问题:
1 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
2 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息;
即会出现安全性 & 效率性的问题。
- 解决方案
使用App应用内广播(Local Broadcast)
有两种方式:
方式一:将全局广播设置成局部广播
- 注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
- 在广播发送和接收时,增设相应权限permission,用于权限验证;
- 发送广播时指定该广播接收器所在的包名(通过**intent.setPackage(packageName)**指定报名),此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
方式二:使用封装好的LocalBroadcastManager类
对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册
示例:
manager = LocalBroadcastManager.getInstance(this);
private void register() {
myLocalReceiver = new MyLocalReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MyLocalReceiver.ACTION);
manager.registerReceiver(myLocalReceiver, filter);
}
private void unregister() {
manager.unregisterReceiver(myLocalReceiver);
}
private void sendStaticBroadcast() {
Intent intent = new Intent();
intent.setAction(MyLocalReceiver.ACTION);
manager.sendBroadcast(intent);
}
五 广播权限
创建自己的使用权限,即在清单文件中添加一个 < permission > 标签来声明自定义权限。
<permission
android:name="com.example.permission.receiver"
android:protectionLevel="signature" />
自定义权限时必须同时指定 protectionLevel 属性值,系统根据该属性值确定自定义权限的使用方式
属性值 | 限定方式 |
---|---|
normal | 默认值。较低风险的权限,对其他应用,系统和用户来说风险最小。系统在安装应用时会自动批准授予应用该类型的权限,不要求用户明确批准(虽然用户在安装之前总是可以选择查看这些权限) |
dangerous | 较高风险的权限,请求该类型权限的应用程序会访问用户私有数据或对设备进行控制,从而可能对用户造成负面影响。因为这种类型的许可引入了潜在风险,所以系统可能不会自动将其授予请求的应用。例如,系统可以向用户显示由应用请求的任何危险许可,并且在继续之前需要确认,或者可以采取一些其他方法来避免用户自动允许 |
signature | 只有在请求该权限的应用与声明权限的应用使用相同的证书签名时,系统才会授予权限。如果证书匹配,系统会自动授予权限而不通知用户或要求用户的明确批准 |
signatureOrSystem | 系统仅授予Android系统映像中与声明权限的应用使用相同的证书签名的应用。请避免使用此选项,“signature”级别足以满足大多数需求,“signatureOrSystem”权限用于某些特殊情况 |
5.1 发送方指定权限
发送方:
<permission
android:name="com.bsoft.ck.permission.mydemo"
android:protectionLevel="normal" />
private void sendStaticBroadcast() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(MyPermissionReceiver.ACTION);
sendBroadcast(intent, MyPermissionReceiver.PERMISSION);
}
接收方:
<uses-permission android:name="com.bsoft.ck.permission.mydemo" />
private void register() {
receiver = new MyPermissionReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MyPermissionReceiver.ACTION);
registerReceiver(receiver, filter, MyPermissionReceiver.PERMISSION, null);
}
private void unRegister() {
unregisterReceiver(receiver);
}
5.2 接收方指定权限:
接收方:
<permission
android:name="com.bsoft.ck.permission.mydemo"
android:protectionLevel="normal" />
private void register() {
receiver = new MyPermissionReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MyPermissionReceiver.ACTION);
registerReceiver(receiver, filter, MyPermissionReceiver.PERMISSION, null);
}
private void unRegister() {
unregisterReceiver(receiver);
}
发送方:
<uses-permission android:name="com.bsoft.ck.permission.mydemo" />
private void sendStaticBroadcast() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(MyPermissionReceiver.ACTION);
sendBroadcast(intent, MyPermissionReceiver.PERMISSION);
}
六 参考
https://blog.youkuaiyun.com/carson_ho/article/details/52973504
https://www.jianshu.com/p/f348f6d7fe59
https://blog.youkuaiyun.com/weixin_34122604/article/details/92349568