一、 BroadcastReceiver 简介
BroadcastReceiver 是用于接收广播的组件用于组件与组件之间进行通信,可以跨应用程
序传递.如操作系统电池电量低会发送一个广播,这样我们的程序里面可以去监听这个广播,
可以关闭程序里面比较耗电的功能,提示用户注意保存进度什么的,还如其它安装新应用等,
还有普通应用程序,例如启动特定线程,文件下载完毕等.
Android 中的广播机制设计的非常出色,很多事情原本需要开发者亲自操作的,现在只
需等待广播告知自己就可以了,大大减少了开发的工作量和开发周期。而作为应用开发者,
就需要熟练掌握 Android 系统提供的一个开发利器,那就是 BroadcastReceiver
注意点
1,发送频率低的可以使用
2,数据量小在可使用
二、 BroadcastReceiver 创建
要创建自己的 BroadcastReceiver 对象,我们需要继承 android.content.BroadcastReceiver,
并实现其 onReceive 方法。
public class MyReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(context, "接收到广播就下课",
Toast.LENGTH_SHORT).show();
}
}
在 onReceive 方法内,我们可以获取随广播而来的 Intent 中的数据,这非常重要,就像
无线电一样,包含很多有用的信息。
在创建完我们的 BroadcastReceiver 之后,还不能够使它进入工作状态,我们需要为它注
册一个指定的广播地址。没有注册广播地址的 BroadcastReceiver 就像一个缺少选台按钮的收
音机,虽然功能俱备,但也无法收到电台的信号
三、 BroadcastReceiver 注册
静态注册
特点: 当程序退出后,广播依然存在,直至应用被卸载
静态注册是在 AndroidManifest.xml 文件中配置的
<receiver android:name="com.example.bcreceiver.MyBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MY_BROADCAST" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
android:exported="true" 是否只接收本应用的广播
动态注册
特点:该广播只在应用中有效,退出后不再接收广播
动态注册需要在代码中动态的指定广播地址并注册
注意,registerReceiver 是 android.content.ContextWrapper 类中的方法,Activity 和 Service
都继承了 ContextWrapper,所以可以直接调用。在实际应用中,我们在 Activity 或 Service 中
注册了一个 BroadcastReceiver,当这个 Activity 或 Service 被销毁时如果没有解除注册,系统
会报一个异常,提示我们是否忘记解除注册了。所以,记得在特定的地方执行解除注册操作
注册
MyBroadcastReceiver mbcr = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(MyBroadcastReceiver.MY_BC_FIRST);
registerReceiver(mbcr, filter);// 注册
解除注册
unregisterReceiver(mbcr);
mbcr = null;
@Override
protected void onDestroy() {
unregestBroadcast();
super.onDestroy();
}
问题来了:
如果有多个接收者都注册了相同的广播地址,又会是什么情况呢,能同时接收到同一条
广播吗,相互之间会不会有干扰呢?这就涉及到普通广播和有序广播的概念了。
四、 广播的发送
广播的发送和接收是跨进程的,一个 app 发送的广播,其他的 app 只要权限和 action
匹配都可以接收到该广播
4.1 发送标准广播
//创建意图对象,并指明 action,那么意图过滤器与这个 action 匹配的广播接受者会
//接收到这个广播
Intent intent = new Intent("com.qianfeng.MY_BROADCAST");
//发送出去广播
sendBroadcast(intent);
注:发送广播的方法都是上下文对象中的方法。
4.2 发送有序广播
Intent intent = new Intent("com.qianfeng.MY_BROADCAST");
//发送有序广播。 参数一:意图对象 参数二:权限。是否需要接受者需要选取才可//以
收到广播
sendOrderedBroadcast(intent, null);
或者:
//由于发送有序广播的时候,中间会有可能被拦截掉,参数三则指定了一个这个广播
//的最终接受者,也就说即使中间有人拦截了广播,则参数三指定的接
//受者也会最终接收到这个广播。
//其余的参数给 null 或 0 即可
sendOrderedBroadcast(it, null, receiver, null, 0, null, null);
注意:
1、发送有序广播的时候,先接到广播的可以通过下面的方法来取消广播,导致后面的优先
级低的广播接收者收不到广播。
abortBroadcast(); // 放弃当前的广播,则优先级低的无法收到当前广播
2、如果优先级高的广播接收者想给优先级低的广播接收者传递数据可以通过下面的方法:
setResult()
setResultData()
setResultCode()
setResultExtra()
五、 广播的分类
普通广播
普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到
广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他
接收者的接收动作。
点击发送后
有序广播
有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受
者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播
可以使用 intent-filter 里面的 android:priority="1000"去解决
使用方式:
①可以通过 android:priority 属性指定广播接收者的接收顺序,值较大的广播接收者会优先接
收
②每个广播接收者是有序接收广播,如果中间使用 abortBroadcast()方法会中止广播,即后
面的接收者不会再收到广播
③每个接收者接收的intent都是从发送方发送的同一个intent对象,每个接收者都是孤立的,
它们之间不可以互相接收 intent
注意:1 、静态注册的广播接收者 者 app 一旦安装在 系统 ,则可以接收到指定的广
播。不管这个 app 有没有启动。 2 、动态注册的广播接收者只有在注册完成后
才能接收到广播,当注册他的组件销毁的时候,广播接收 者 也应该解除注册。
六、 实战运用- 读取短信内容
定义接收者
public class SmsRecevier extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(context, "有短消息来了", Toast.LENGTH_LONG).show();
// 获取短消息实际的内容
Bundle b = intent.getExtras();
// primary data user station (PDUS 主数据用户站)
Object[] pdus = (Object[]) b.get("pdus");
// 将pdus的数据解析
SmsMessage[] smsMsgs = new SmsMessage[pdus.length];
// 循环遍历转换成SmsMessage数组
for (int i = 0; i < smsMsgs.length; i++) {
// Protocol Data Unit (协议数据单元)
smsMsgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
String phoneNumber = null;
String data = null;
StringBuilder builder = new StringBuilder();
// 循环遍历从SmsMessage数组中获取内容
for (SmsMessage sms : smsMsgs) {
// 获取号码
phoneNumber = sms.getDisplayOriginatingAddress();
// 获取短信内容
data = sms.getDisplayMessageBody();
// 每次拿到的内容存放到StringBuilder中,最后显示完整的短信内容
builder.append(data);
}
Log.i("123", "短信号码:" + phoneNumber);
Log.i("123", "短信内容:" + builder.toString());
// 后续的接收器就终止接收短信的逻辑
// 短信发送的是有序广播
abortBroadcast();
// 自定义Notification显示内容
NotificationCompat.Builder nb = new NotificationCompat.Builder(context);
nb.setContentTitle(phoneNumber);
nb.setContentText(builder.toString());
nb.setSmallIcon(R.drawable.ic_launcher);
NotificationManager nm = (NotificationManager) context
.getSystemService(Service.NOTIFICATION_SERVICE);
//消息通知的发布
nm.notify(2, nb.build());
}
}
静态注册广播
<receiver android:name="com.qianfeng.day23_broadcastsystem.SmsRecevier">
<intent-filter android:priority="3000">
<!-- 接收短消息的条件(频道) -->
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
得到系统权限
<!-- 申请获取短消息的权限 -->
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
七、 实战运用- 读 电量信息
如果我们阅读软件,可能是全屏阅读,这个时候用户就看不到剩余的电量,我们就可以
为他们提供电量的信息。要想做到这一点,我们需要接收一条电量变化的广播,然后获取百
分比信息
public class BatteryReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
// 电池电量发生改变的时候进行监听
// 获取电池电量数据
// 当前电量(第一个参数当前的电量key值,第二个参数默认值)
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
// 最大电量
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
// 要计算电池电量的百分比
int percent = level * 100 / scale;
Log.i("123", "当前电量:" + level);
Log.i("123", "最大电量:" + scale);
Log.i("123", "当前电量百分比:" + percent);
// 关于状态的判断(是否在充电)
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 0);
Log.i("123", "status:" + status);
switch (status) {
case BatteryManager.BATTERY_STATUS_CHARGING:// 充电状态
Log.i("123", "充电");
break;
case BatteryManager.BATTERY_STATUS_DISCHARGING:// 放电状态
Log.i("123", "放电");
break;
case BatteryManager.BATTERY_STATUS_UNKNOWN:// 未知状态
Log.i("123", "未知");
break;
default:
break;
}
// 性能的参数(和Health有关)
int health = intent.getIntExtra(BatteryManager.EXTRA_HEALTH, 0);
// BatteryManager.BATTERY_HEALTH_GOOD 性能ok
switch (health) {
case BatteryManager.BATTERY_HEALTH_COLD:
Log.i("123", "电池温度过低");
break;
case BatteryManager.BATTERY_HEALTH_DEAD:
Log.i("123", "电池坏掉了");
break;
case BatteryManager.BATTERY_HEALTH_GOOD:
Log.i("123", "电池健康");
break;
case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
Log.i("123", "电池电压过高");
break;
case BatteryManager.BATTERY_HEALTH_OVERHEAT:
Log.i("123", "电池温度过高");
break;
case BatteryManager.BATTERY_HEALTH_UNKNOWN:
Log.i("123", "电池温度过低");
break;
case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
Log.i("123", "电池温度过低");
break;
}
}
}
}
动态注册广播接收者
receiver2 = new BatteryReceiver();
IntentFilter filter = new IntentFilter();
//电池电量
IntentFilter filter2 = new IntentFilter();
filter2.addAction(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(receiver2, filter2);
<!-- 电池电量状态的权限 -->
<uses-permission android:name="android.permission.BATTERY_STATS"/>
注意:在Activity 销毁时应该注销广播
八、 实战运用- 监控开机广播
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//监听开机的广播接收器
String action = intent.getAction();
if(action.equals(Intent.ACTION_BOOT_COMPLETED)){
Log.i("123", "监听到开机完成");
//启动界面
Intent intent2 = new Intent(context,MainActivity.class);
context.startActivity(intent2);
}
}
}
<!-- 开机完毕的条件 -->
<receiver
android:name="com.qianfeng.day23_broadcastsystem.BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
九、 实战运用- 监控网络状态
在某些场合,比如用户浏览网络信息时,网络突然断开,我们要及时地提醒用户网络已
断开。要实现这个功能,我们可以接收网络状态改变这样一条广播,当由连接状态变为断开
状态时,系统就会发送一条广播,我们接收到之后,再通过网络的状态做出相应的操作
定义广播接收者
public class ConnectivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// 网络状态的查看
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Service.CONNECTIVITY_SERVICE);
// 处于活跃状态的网络信息
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null && info.isAvailable()
&& info.isConnectedOrConnecting()) {
//这时候网络处于里连接ok的状态
//获取网络的名称(WIFI,MOBILE)
String name = info.getTypeName();
Toast.makeText(context, name+"正在连接或已连接",
Toast.LENGTH_LONG).show();
}else {
//没有正常连接的网络
Toast.makeText(context, "网络连接不正常,查看网络设置",
Toast.LENGTH_SHORT).show();
}
}
}
注册一下这个接收者的信息
<!-- 关于网络状态的条件 -->
<receiver
android:name="com.qianfeng.day23_broadcastsystem.ConnectivityReceiver">
<intent-filter >
<!-- 网络连接发生变化的时候 -->
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
在接收方法中我们使用到了网络状态相关的 API,所以需要声明相关的权限才行
<!-- 网络状态的权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
十、 实战运用- 监控来电和去电信息
定义广播接收者
public class PhoneReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// 实现对应的电话处理
String action = intent.getAction();
if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
// 打出电话的处理
String phoneNumber = intent
.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Log.i("123", "打出电话的电话号码:" + phoneNumber);
} else { // 来电的处理
Bundle bundle = intent.getExtras();
// key在TelephonyManager
String comingNumber = bundle
.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.i("123", "来电的号码:" + comingNumber);
// 获取当前的状态
TelephonyManager tm = (TelephonyManager) context
.getSystemService(Service.TELEPHONY_SERVICE);
// 来电状态的获取
int state = tm.getCallState();
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: // 电话挂断的状态
Toast.makeText(context, "电话挂断了",
Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:// 正在接听电话
Toast.makeText(context, "正在接听电话",
Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_RINGING: // 来电响铃
Toast.makeText(context, "来电响铃",
Toast.LENGTH_LONG).show();
default:
break;
}
}
}
}
注册广播接收者
<receiver android:name="com.qianfeng.day23_broadcastsystem.PhoneReceiver">
<intent-filter >
<!-- 打出电话的条件 -->
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
<!-- 来电的条件 -->
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
加入权限
<!-- 申请得到打出电话信息的权限 -->
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!-- 申请接收来电状态的权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
十一、 实战运用- 监控 屏幕状态
定义广播接收者
public class ScreenOnOffReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if(action.equals(Intent.ACTION_SCREEN_OFF)){
//屏幕休眠状态
Log.i("123", "屏幕休眠了");
}else if(action.equals(Intent.ACTION_SCREEN_ON)){
//屏幕唤醒状态
Log.i("123", "屏幕唤醒了");
}else if(action.equals(Intent.ACTION_USER_PRESENT)){
//屏幕解锁
Log.i("123", "屏幕解锁");
}
}
}
注册广播
receiver = new ScreenOnOffReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_PRESENT);
//动态注册广播接收器
registerReceiver(receiver, filter);