BroadcastReceiver是Android的四大组件之一,是用于接收来自系统和应用的广播对并对其进行响应的组件。
标题
BroadcastReceiver的四种类型
1、普通广播
普通广播是一种完全异步执行**的广播,在广播发出之后,所有的广播接收器几乎会同时接收到这条广播消息。此类广播效率较高而且不能截断。
自定义 intent的广播(最常用)。发送广播使用如下:
Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);
若被注册了的广播接收者中注册时intentFilter的action与上述匹配,则会接收此广播(即进行回调onReceive())。如下MBroadcastReceiver则会接收上述广播
若发送广播有相应权限,那么广播接收者也需要相应权限
<receiver
android:name=".MBroadcastReceiver" >
<intent-filter>
<action android:name="BROADCAST_ACTION" />
</intent-filter>
</receiver>
2、有序广播
有序广播是一种同步执行(顺序)的广播,广播发出之后,优先级高的广播接收器就可以先接收到广播消息,执行完该广播接收器的逻辑后,可以选择截断正在传递的广播或者继续传递,如果广播消息被截断,之后的广播接收器则无法收到广播消息。有序广播中的“有序”是针对广播接收者而言的。有序广播的定义过程与普通广播无异,发送有序广播方法:sendOrderedBroadcast()
有序广播的接收者们将按照事先声明的优先级依次接收,数越大优先级越高(取值范围:-1000~10000)
android:priority 定义广播接收者的优先级
<intent-filter android:priority="n"/>
或调用ntentFilter对象的setPriority()
设置
终止广播:调用abortBroadcast()方法
终止广播,一旦终止后面接收者就无法接收广播
3、系统广播
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 |
4、App应用内广播或本地广播(LocalBroadcast)
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内与之相匹配的有效广播接收器中。
注意事项:
- 对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册
- 若在广播中启动Activity,则需要为intent加入FLAG_ACTIVITY_NEW_STACK标记,否则会报错(需要一个栈来存放一个新打开的activity)
- 若广播中弹出AlertDialog,需要设置对话类型为TYPE_SYSTEM_ALTER,否则对话框无法弹出
BroadcastReceiver的注册
静态注册
在application标签
中注册
<receiver android:name=".MyReceived">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
android 6.0前静态申请权限,6.0后需动态申请权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
动态注册
myReceived = new MyReceived();
IntentFilter intentFilter = new IntentFilter();
//接收网络状态发生变化的广播
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//动态注册广播
registerReceiver(myReceived,intentFilter);
...
protected void onDestroy() {
super.onDestroy();
// 一定要取消广播注册
// unregisterReceiver(myReceived);
}
动态注册广播的缺点:需要程序启动才可以接收广播
实例
发送广播的简单例子
<receiver android:name=".MyReceived">
<intent-filter>
<action android:name="com.demo.MY_BROADCAST" />
</intent-filter>
</receiver>
package com.demo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyReceived extends BroadcastReceiver {
private final String ACTION_BOOT = "com.demo.MY_BROADCAST";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"收到广播啦~",Toast.LENGTH_SHORT).show();
}
}
Button send = findViewById(R.id.send);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
sendBroadcast(new Intent("com.demo.MY_BROADCAST"));
}
});
LocalBroadcast的例子
//创建LocalBroadcastManager(局部通知管理器)对象
final LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button send = findViewById(R.id.send);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//发送广播
localBroadcastManager.sendBroadcast(new Intent("com.LocalBroadcastManager"));
}
});
//动态注册本地广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.LocalBroadcastManager");
myReceived = new MyReceived();
localBroadcastManager.registerReceiver(myReceived,intentFilter);
使用广播的注意事项
不要在广播里添加过多逻辑或者进行任何耗时操作,因为在广播中是不允许开辟线程的, 当onReceiver( )方法运行较长时间(超过10秒)还没有结束的话,那么程序会报错(ANR), 广播更多的时候扮演的是一个打开其他组件的角色,比如启动Service,Notification提示, Activity等!