一 概述
BroadcastReceiver是安卓四大组件之一,它是一个全局的监听器,它能够接收安卓系统、App内以及其它App发出的广播。安卓的广播采用观察者模式,基于消息的 订阅 / 发布 事件模型,因此广播的发送者和接收者完全解耦,易于使用和扩展。
二 BroadcastReceiver使用流程
- 自定义BroadcastReceiver
我们要想使用BroadcastReceiver需要先定义个类继承BroadcastReceiver然后实现其public void onReceive(Context context, Intent intent)
方法。
广播接收器接收到相应的广播后会自动回调onReceive方法,该方法运行在主线程中,因此不宜做耗时操作。
一般情况下在onReceive方法中会涉及到与其它组件的交互,比如打开Service、启动Activity等。
代码示例
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获得意图(intent)中传递过来的action
String action = intent.getAction();
Log.e(getClass().getSimpleName(),"action:"+action);
//可以在意图(intent)中使用bundle传递参数
Bundle extras = intent.getExtras();
String id = extras.getString("id");
String name = extras.getString("name");
Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
}
}
- BroadcastReceiver注册
我们自定义了BroadcastReceiver之后,还需要注册一下才能收到对应的广播消息。广播的注册有两种方式分别为:静态注册和动态注册。
- 静态注册
静态注册的方式是直接在xml文件中注册,示例如下:
<receiver
//广播接收者的类名路径
android:name=".MyReceiver"
//发送广播的app应该具有的权限
android:permission="String"
//广播是否可用,为true时系统才会初始化该广播
android:enabled="true"
//是否能接受其他app发出的广播,当有intent-filter时默认为true
android:exported="true">
<intent-filter>
//接受到的广播类型,可以使用系统已有的广播类型也可以自定义
<action android:name="com.jrmf360.sendrp"/>
</intent-filter>
</receiver>
当app启动时系统会自动实例化该广播类,并注册到系统中
- 动态注册
动态注册就是在代码中注册,这种方式比较灵活,当用到广播时才去注册且可以反注册,广播不会一直存在,节约内存。看个示例:
@Override
protected void onResume() {
super.onResume();
broadcastReceiver = new MyBroadcastReceiver();
//创建意图过滤器并添加Action
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.jrmf360.sendrp");
//注册BroadcastReceiver
registerReceiver(broadcastReceiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
}
class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.e(getClass().getSimpleName(),"action:"+action);
Bundle extras = intent.getExtras();
String id = extras.getString("id");
String name = extras.getString("name");
Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
}
}
在onResume方法中注册BroadcastReceiver在onPause方法中取消注册BroadcastReceiver。注册和反注册必须成对出现以免造成内存泄漏。
- 广播种类
广播分为普通广播、有序广播、系统广播和App内广播,具体说明如下:
- 普通广播
开发者自定义的广播,发送广播如下:
//创建意图并设置Action
Intent intent = new Intent();
//只有当广播的接收Action于此匹配才能接收到该广播
intent.setAction("com.jrmf360.sendrp");
//使用bundle传递参数
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//发送广播
sendBroadcast(intent);
- 有序广播
发送出去的广播按照先后顺序被广播接收者接收,有序是针对广播接收者而言的。
接收规则:
按照Priority属性值从大-小排序
如果Priority属性值相同动态广播优先
高优先级的广播可以更改广播中的数据然后通过public final void setResult (int code, String data, Bundle extras)
方法把更改后的结果传递给下一级广播;高优先级的广播还可以通过调用abortBroadcast()
方法拦截广播,后续所有的广播接收者都将收不到该广播。 - 系统广播
安卓系统内置了很多系统广播,只要涉及到手机的基本操作基本都会发出对应的广播,例如:开关机、亮屏以及音量变化等。每个广播都有Intent - Filter(包括具体的action),安卓中一些内置的Action如下:
系统操作 | action |
---|---|
网络变化 | android.net.conn.CONNECTIVITY_CHANGE |
充电时或电量发生变化 | Intent.ACTION_BATTERY_CHANGED |
屏幕被关闭 | Intent.ACTION_SCREEN_OFF |
屏幕被打开 | Intent.ACTION_SCREEN_ON |
系统启动完成后(仅广播一次) | Intent.ACTION_BOOT_COMPLETED |
- 应用内广播
当我们使用自定义广播时可能会出现一些问题:
其它App发出和我们广播接收者相同的Action导致我们不断地收到广播并处理;
其它App定义和我们相同的广播接收者导致我们的广播信息暴露。
要解决以上问题我们就需要使用到应用内广播,所谓应用内广播保证了我们App发出的广播在其它App内的广播接收者收不到并且其它App发出的广播我们App内的广播接收者不接收。App内广播的实现有两种方式:
方式一:
在注册广播时android:exported="false"这样就接收不到其它App发出的广播了,如下:
<receiver
//广播接收者的类名路径
android:name=".MyReceiver"
//发送广播的app应该具有的权限
android:permission="com.jrmf360.permission.send"
//广播是否可用,为true时系统才会初始化该广播
android:enabled="true"
//是否能接受其他app发出的广播,当有intent-filter时默认为true
android:exported="false">
<intent-filter>
//接受到的广播类型,可以使用系统已有的广播类型也可以自定义
<action android:name="com.jrmf360.sendrp"/>
</intent-filter>
</receiver>
单单接收不到其它App发出的广播还不能满足我们的要求,还需要我们自己发出的广播其它App接收不到,这就需要在发送广播的时候设置包名:
Intent intent = new Intent();
intent.setAction("com.jrmf360.sendrp");
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//设置包名
intent.setPackage("应用包名");
sendBroadcast(intent);
方式二:
使用LocalBroadcastManager也可以实现应用内广播,需要注意的是只有动态注册的广播才能使用LocalBroadcastManager并且广播的注册,发送和反注册都要使用LocalBroadcastManager.getInstance(this)的单例对象去完成。示例如下:
@Override
protected void onResume() {
super.onResume();
broadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.jrmf360.sendrp");
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_sendBroadcast){
Intent intent = new Intent();
intent.setAction("com.jrmf360.sendrp");
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
sendBroadcast(intent);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
- 广播的权限
广播的权限分为广播发送者要求广播接收者具有的权限和广播接收者要求广播发送者具有的权限。
- 广播接收者要求广播发送者具有的权限
在我们在xml文件中注册广播接收者的时候有这样一行属性android:permission="String"
我们可以在此添加对应的权限,这就要求发出广播的App必须拥有此权限,下面演示一下流程:
先在xml中声明广播接收者并添加权限
<receiver
android:name=".MyReceiver2"
//添加自定义权限
android:permission="com.jrmf360.permission.send2"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.jrmf360.sendrp"/>
</intent-filter>
</receiver>
然后我们需要在xml文件中声明该权限
<!--权限声明-->
<permission android:name="com.jrmf360.permission.send2"/>
最后广播发送者所在的App需要在xml中注册该权限
<uses-permission android:name="com.jrmf360.permission.send2"/>
- 广播发送者要求广播接收者具有的权限
首先调用发送广播方法时我们需要传入对应的权限
sendBroadcast(intent,"com.jrmf360.rp");
接着在xml文件中声明该权限
<permission android:name="com.jrmf360.rp"/>
最后广播接收者所在的App需要在xml中注册该权限
<!--权限注册-->
<uses-permission android:name="com.jrmf360.rp"/>
广播的权限是对广播接收者随意接收广播的限制和保护。
总结:广播可以跨线程、进程和App通信,我们在开发过程中可以很方便的利用它来传递消息。但我们也不能滥用广播可能会导致接收消息的地方过多不好维护而且广播接收者相对是个比较重的组件,比如线程间通信我们可以通过Handler解决,没有必要使用广播。