四大核心组件之BroadcastReceiver
1.BroadcastReceiver简介
BroadcastReceiver也就是"广播接收者”的意思,顾名思义,它就是用来接收来自系统和应用中的广播。
在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能:当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。
Android中的广擂机制设计的非常出色。很多事情原本需要开发者亲自操作的,现在只需等待广播告知自己就可以了,大大减少了开发的工作量和开发周期。而作为应用开发者,就需要数练掌握Android系统提供的一个开发利器,那就是BroadcastReceiver。
2.广播接收器的类型
(1)Normal broadcasts:默认广播
发送一个默认广播使用Context.sendBroadcast()方法,普通广播对于多个接受者来说是完全异步的,通常每个接收者都无需等待就可以接收到广播,接受者相互之间不会有影响。对于这种广播,接受者无法终止广播,即无法阻止其他接受者的接收动作。
(2)Ordered broadcasts:有序广播
发送一个有序广播使用Context.sendOrderedBroadcast()方法,有序广播比较特殊,它每次只发送到优先级较高的接收者那边,然后由优先级高的接受者再传播到优先级低的接受者那里,优先级高的接受者有能力终止这个广播。
(3)Sticky broadcasts:粘性广播
处理完之后的Intent,依然存在,直到你把它去掉。
3.广播接收器的创建步骤
构建一个Intent,使用sendBroadcast方法发送广播
//发送一个普通的广播
public void sendNormalClick(View v){
Intent intent=new Intent("com.action.MY_BROADCASTS");//设置action当做广播频道
intent.putExtra("info","这是一个普通广播频道");//发送数据
this.sendBroadcast(intent);
}
定义一个广播接收器,该广播接收器继承BroadcastReceiver,并且覆盖onReceive()方法来响应事件
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/*
* 自定义的广播接收器
* */
public class MyNormalReceiver extends BroadcastReceiver {
public MyNormalReceiver(){
}
//接收
@Override
public void onReceive(Context context, Intent intent) {
String info=intent.getStringExtra("info");//接收信息
Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
}
}
注册该广播接收器,我们可以在代码中注册,也可以在AndroidManifest.xml配置文件中注册
<receiver
android:name=".MyNormalReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.action.MY_BROADCASTS"></action>
</intent-filter>
</receiver>
4.注册广播接收器的两种方式
静态注册:静态注册是在AndroidManifest.xml配置文件中注册
动态注册:需要在代码中动态指定广播地址并注册,通常我们是在Activity或Service注册一个广播。
//在该方法中进行广播注册
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction("com.action.MY_BROADCASTS");
registerReceiver(myReceiver, filter);
}
//在该方法中解除广播
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myReceiver);
}
//发送一个广播
public void sendNormalClick(View v){
Intent intent=new Intent("com.action.MY_BROADCASTS");
this.sendBroadcast(intent);
}
广播接收器
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/*
* 动态注册的广播接收器
* */
public class MyReceiver extends BroadcastReceiver {
public MyReceiver(){
}
@Override
public void onReceive(Context context, Intent intent) {
String info=intent.getStringExtra("info");//接收信息
Toast.makeText(context, "动态注册的广播接收器", Toast.LENGTH_SHORT).show();
}
}
注意:
在这个方法发来的广播中,代码注册方式中,收到法广播的先后和著名优先级最高的他们的先后是随机的。如果没有优先级,代码接收会优先接收。
5.有序广播
发送有序广播: sendOrderedBroadcast()
在注册广播中的中使用android:priority属性。这个属性的范围在-1000到1000,数值越大, 优先级越高。
在广播接收器中使用setResultExtras方法将一个Bundle对象设置为结果集对象。传递到下一个接收者那里,这样优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。
使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要 求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有栏截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。
终止广播传递:abortBroadcast();
同级别接收先后是随机的,再到级别低的接收广播;如果衔接受到的把广播截断了,同级别以外的接受者是无法收到该广播的。
在这个方法发来的广播中(代码注册方式),收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为优先
构建一个Intent,发送广播
//发送一个有序广播
public void sendOrderClick(View v){
Intent intent=new Intent("com.action.MY_BROADCASTS2");
//参数:intent,接收权限
this.sendOrderedBroadcast(intent,null);
}
定义两个个广播接收器,分别为1,2
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/*
* 有序广播接收器
* */
public class MyOrderedReceiver extends BroadcastReceiver {
public MyOrderedReceiver(){
}
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "有序广播-1", Toast.LENGTH_SHORT).show();
}
}
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyOrderedReceiver2 extends BroadcastReceiver {
public MyOrderedReceiver2(){
}
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "有序广播-2", Toast.LENGTH_SHORT).show();
}
}
注册该广播接收器,设置优先级
<receiver
android:name=".MyOrderedReceiver2"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.action.MY_BROADCASTS2" />
</intent-filter>
</receiver>
<receiver
android:name=".MyOrderedReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="200">
<action android:name="com.action.MY_BROADCASTS2" />
</intent-filter>
</receiver>
在广播接收器中使用setResultExtras方法将一个Bundle对象设置为结果集对象,传递到下一个接收者。
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
/*
* 有序广播接收器
* */
public class MyOrderedReceiver extends BroadcastReceiver {
public MyOrderedReceiver(){
}
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "有序广播-1", Toast.LENGTH_SHORT).show();
Bundle data=new Bundle();
data.putString("info","广播-1");
this.setResultExtras(data);
}
}
中断有序广播
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
/*
* 有序广播接收器
* */
public class MyOrderedReceiver extends BroadcastReceiver {
public MyOrderedReceiver(){
}
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "有序广播-1", Toast.LENGTH_SHORT).show();
Bundle data=new Bundle();
data.putString("info","广播-1");
this.setResultExtras(data);
//中断有序广播
this.abortBroadcast();
}
}
广播中断,便不会在出现广播下一个广播
6.粘性广播
发送粘性广播使用:sendStickyBroadcast(intent);
发这个广播需要权限
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
去掉使用removeStickyBroadcast(intent);方法
sendStickyOrderedBroadcast():这个方法有有序广播的特性也有粘性广播的特性;在这个方法发来的广播中,代码注册方式中,收到广播先后次序为:注明优先级的、代码注册的、没有优先级的;如果都没有优先级,代码注册收到为最先。
创建Intent
//发送一个粘性的广播
public void sendStickyClick(View v){
Intent intent=new Intent("com.action.MY_BROADCASTS3");
this.sendStickyBroadcast(intent);
}
//启动接收粘性广播Activity
public void openStickyActivityClick(View v){
Intent intent=new Intent(this,Main2Activity.class);
startActivity(intent);
}
定义接收器
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyStickyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受一个粘性广播", Toast.LENGTH_SHORT).show();
}
}
创建一个新的activity,并注册接收器
package com.example.broadcastreceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
public class Main2Activity extends AppCompatActivity {
private MyStickyReceiver myStickyReceiver=new MyStickyReceiver();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter=new IntentFilter("com.action.MY_BROADCASTS3");
registerReceiver(myStickyReceiver,filter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myStickyReceiver);
}
}
注意:
需要在清单文件中设置权限
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
注意:
在重写onResume()方法时,不要执行长时间的操作,不能超过10秒,否则会抛出异常,若要执行长时间操作需要用service组件去完成。
7.接收系统广播
1.开机启动服务
我们经常会有这样的应用场合,比如消息推送服务,需要实现开机启动的功能。要实现这个功能,我们就可以订阅系统“启动完成”这条广播,接收到这条广播后我们可以启动自己的服务了。
定义广播接收器
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/*
* 开机启动的广播接收器
* */
public class MyReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "开机啦", Toast.LENGTH_SHORT).show();
}
}
注册开机广播地址
<receiver
android:name=".MyReceiver2"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
设置权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
2.网络状态变化
比如用户浏览网络信息时,网络突然断开,我们要及时地提醒用户网络已断开。要实现这个功能,我们可以接收网络状态改变这样一条广播,当由连接状态变为断开状态时,系统就会发送一条广播,我们接收到之后,再通过网络的状态做出相应的操作。
定义广播接收器
package com.example.broadcastreceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.widget.Toast;
public class MyReceiver3 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm= (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info=cm.getActiveNetworkInfo();
if (info!=null){
String name=info.getTypeName()+"";
Toast.makeText(context, name, Toast.LENGTH_LONG).show();
}
}
}
注册
<receiver
android:name=".MyReceiver3"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.CONFIGURATION_CHANGED"></action>
</intent-filter>
</receiver>
访问网络状态权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
3.电量变化
如果我们在使用阅读软件,可能是全屏阅读,这个时候用户看不到剩余的电量,我们可以为他们提供电量信息。要想做到这一点,我们需要接受一条电量变化的广播,然后获取百分比信息。
定义广播接收器
/*
* 监测电量变化
* */
public class MyReceiver4 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int curr=intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);//当前电量
int total=intent.getIntExtra(BatteryManager.EXTRA_SCALE,1);//从电量
int percent=curr*100/total;
Toast.makeText(context, "当前电量为"+percent, Toast.LENGTH_SHORT).show();
}
}
注册广播接收器
<receiver
android:name=".MyReceiver4"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"></action>
</intent-filter>
</receiver>
要立即获取电量的,而不是等电量变化的广播,可以使用以下代码获取:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//立即获取电量信息的方法
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("android.intent.action.BATTERY_CHANGED");
Intent batteryIntent=getApplicationContext().registerReceiver(null,intentFilter);
int curr=batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);//当前电量
int total=batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE,1);//从电量
int percent=curr*100/total;
Toast.makeText(this, "当前电量为"+percent, Toast.LENGTH_SHORT).show();
}