学习Android一段时间,来总结下BroadcastReceiver的使用方法。
一、什么是广播,广播接受者
Android系统中,广播体现在很多方面,例如开机启动后会发出一条广播,当接收到这条广播就能实现开机启动服务的功能。当网络状态发生变化时产生一条广播,接收到后能及时做出提示和保存数据等操作。当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。
二、广播注册的两种方式
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.hlh.base"/>
<category android:name="android.intent.category.DEFAULT"/>
intent-filter>
receiver>
如果当注册一些如短信的广播时,需要配置权限
<receiver>
< intent-filter android:priority= "1000">
< action android:name= " android.provider.Telephony.SMS_RECEIVED"/>
</ intent-filter >
</receiver>
//加上权限
< uses-permission android:name= "android.permission.RECEIVE_SMS"/>
< uses-permission android:name= "android.permission.SEND_SMS"/>
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.hlh.base");
registerReceiver(receiver, filter);
但是代码中注册也分两种情况
注册方法一:registerReceiver(BroadcastReceiver receiver, IntentFilter fileter);第一个参数是我们要处理广播的BroadcastReceiver;第二个参数是意图过滤器。
注册方式二:registerReceiver(receiver, filter, broadcastPermission, scheduler)第一个参数是 BroadcastReceiver (广播接收者,可以是系统的,也可以是自定义的);第二个参数是意图过滤器;第三个参数是广播权限;第四个参数是 Hander ;
note:权限重复问题,如果AndroidManifest.xml注册了该权限,用该方法再注册,则receiver无法接收到广播;如果AndroidManifest.xml没有注册该权限,该方法注册也无法收到广播;当该方法没有注册权限,AndroidManifest.xml注册的时候,receiver可以接收到广播。
三、Android中使用广播的一般步骤
public class MyBroadcastReceiver extends BroadcastReceiver{
public static final String TAG= "broadcastreceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "接收到广播" + intent.getStringExtra("msg"));
}
}
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.hlh.base");
registerReceiver(receiver, filter);
Intent intent = new Intent("com.hlh.base");
intent.putExtra("msg", "love Android");
sendBroadcast(intent);
四、广播类型
public class FirstReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d(MyConfig.LOG, "FirstReceiver " + intent.getStringExtra("msg"));
}
}
public class SecondReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d(MyConfig.LOG, "SecondReceiver " + intent.getStringExtra("msg"));
}
}
public class ThirdReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d(MyConfig.LOG, "ThirdReceiver " + intent.getStringExtra("msg"));
}
}
然后在三个广播接受者的onReceive()中加入abortBroadcast()方法终止广播。再次点击发送按钮,我们会发现,控制台中三个接收者仍然都打印了自己的日志,因此接收者并不能终止广播。
public class FirstReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d(MyConfig.LOG, "FirstReceiver " + intent.getStringExtra("msg"));
Bundle bundle = new Bundle();
bundle.putString("msg", intent.getStringExtra("msg") + " @FirstReceiver");
setResultExtras(bundle);
}
}
SecondReceiver.java
public class SecondReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d(MyConfig.LOG, "SecondReceiver " + getResultExtras(true).getString("msg"));
Bundle bundle = new Bundle();
bundle.putString("msg", getResultExtras(true).getString("msg") + " @SecondReceiver");
setResultExtras(bundle);
}
}
ThirdReceiver.java
public class ThirdReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d(MyConfig.LOG, "ThirdReceiver " + getResultExtras(true).getString("msg"));
}
}
在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法将一个Bundle对象设置为结果集对象,传递到下一个接收者那里,这样以来,优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。
代码改完之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:
<receiver android:name=".FirstReceiver">
<intent-filter android:priority="1000">
<action android:name="com.hlh.base"/>
<category android:name="android.intent.category.DEFAULT"/>
intent-filter>
receiver>
<receiver android:name=".SecondReceiver">
<intent-filter android:priority="500">
<action android:name="com.hlh.base"/>
<category android:name="android.intent.category.DEFAULT"/>
intent-filter>
receiver>
<receiver android:name=".ThirdReceiver">
<intent-filter android:priority="0">
<action android:name="com.hlh.base"/>
<category android:name="android.intent.category.DEFAULT"/>
intent-filter>
receiver>
最后,修改发送广播的代码:
Intent intent = new Intent("com.hlh.base");
intent.putExtra("msg", "love Android");
sendOrderedBroadcast(intent, "com.hlh.base.MY_BROADCASTRECEIVER_PERMISSION");
注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。
所以我们在AndroidMainfest.xml中定义一个权限:
<permission
android:name="com.hlh.base.MY_BROADCASTRECEIVER_PERMISSION"
android:protectionLevel="normal"/>
然后声明使用了此权限:
<uses-permission android:name="com.hlh.base.MY_BROADCASTRECEIVER_PERMISSION"/>
然后我们点击发送按钮发送一条广播,控制台打印如下:
我们看到接收是按照顺序的,第一个和第二个都在结果集中加入了自己的标记,并且向优先级低的接收者传递下去。
既然是顺序传递,试着终止这种传递,看一看效果如何,我们修改FirstReceiver的代码,在onReceive的最后一行添加以下代码:abortBroadcast();
再次运行控制台只打印了
此次,只有第一个接收者执行了,其它两个都没能执行,因为广播被第一个接收者终止了。
五、实例
我们经常有这样的应用场合,比如消息推送服务,需要开机启动就能实现。实现该功能,必须订阅开机启动完成这条广播,接收到这条广播后,即可启动我们的消息推送服务。接收开机完成的广播接收者BootCompleteReceiver.java和消息推送服务MsgPushService.java如下所示
public class BootCompleteReceiver extends BroadcastReceiver{
private static final String MSG = "BootCompleteReceiver";
@Override
public void onReceive(Context context, Intent intent) {
//启动消息推送服务
Intent service = new Intent(context, MsgPushService.class);
context.startService(service);
Log.d(MSG, "Boot Complete , Starting MsgPushService...");
}
}
public class MsgPushService extends Service{
private static final String TAG = "MsgPushService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate complete...");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand complete... ");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
接下来在AnroidManifest.xml配置相关信息
<receiver android:name=".BootCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
intent-filter>
receiver>
<service android:name=".MsgPushService"/>
我们看到BootCompleteReceiver注册了“android.intent.action.BOOT_COMPLETED”这个开机广播地址,从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是我们再声明使用下面的权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
经过上面的几个步骤之后,我们就完成了开机启动的功能,将应用运行在模拟器上,然后重启模拟器,控制台打印如下:
public class NetworkStateReceiver extends BroadcastReceiver{
private static final String TAG = "NetworkStateReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "network state changed");
if (!isNetworkAvailable(context)) {
Toast.makeText(context, "network disconnected error", Toast.LENGTH_SHORT).show();
}
}
/**
* 网络是否可用
*/
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager ctm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo[] info = ctm.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if(info[i].getState() == NetworkInfo.State.CONNECTED)
return true;
}
}
return false;
}
}
注册接收者的信息:
<receiver android:name=".NetworkStateReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
intent-filter>
receiver>
因为在isNetworkAvailable方法中我们使用到了网络状态相关的API,所以需要声明相关的权限才行,下面就是对应的权限声明:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
六、注意的问题