一. BroadcastReceiver简介
1.1 BroadcastReceiver定义(What?)
Android四大组件之一,是一种全局的监听器。
1.2 BroadcastReceiver作用(Why?)
- 监听系统或应用发出(或接收)的广播信息,然后根据相应信息做逻辑处理。
常用监听系统广播信息如有电话打来时、网络状态发生变化时、手机电量发生变化时等。
- 发送或接收少量或发送频率较低的数据。
因为使用BroadcastReceiver发送/接收大量数据开销较大,且由于其消息是异步的,所以有可能发生数据接收不到的情况。
- 其它作用:
- 不同组件间的通信,包括同应用和不同应用间。如常用的Activity与Service间通信。
- 多线程间通信。
二. 广播(Broadcast)的分类及使用(How?)
Android中广播主要可以分为五类:
- 普通广播(Normal Broadcast)
- 有序广播(Ordered Broadcast)
- 系统广播(System Broadcast)
- 本地广播(Local Broadcast)
- 粘性广播(Sticky Broadcast)
2.1 普通广播(Normal Broadcast)
普通广播也就是开发者常用的自定义的广播,使用步骤:
- 首先需要一个接收广播的类,BroadcastReceiver是一个抽象类,抽象方法
onReceive()
必须实现:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"MyBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
}
}
- 如同其它的组件一样,自定义的BroadcastReceiver也需要在
manifest
中进行注册,其中<intent-filter>
中的<action
用于指定该receiver
要接收的广播的类型,类型不对就接收不到了。此外还可以动态注册,后面再说:
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.sky.intent.action.MYBROADCASTRECEIVER"/>
</intent-filter>
</receiver>
android:enabled="true"
表示是否可用
android:exported="true"
表示是否接收其它应用发送的广播,如果该receiver有<intent-filter>
属性,则默认为true,如果没有,则android:exported
默认为false。
- 然后需要发送广播让MyBroadcastReceiver来接收,简单地定义一个按钮,点击设置Intent对象的action并发送广播。
<Button
android:onClick="sendBroadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送广播"/>
这里设置Intent的action必须与MyBroadcastReceiver定义的action保持一致,如同其它组件运行一样直接调用。
public static String ACTION_MYBROADCAST = "com.sky.intent.action.MYBROADCASTRECEIVER";
...
public void sendBroadcast(View view){
Intent intent = new Intent(ACTION_MYBROADCAST);
sendBroadcast(intent);
}
2.2 有序广播(Normal Broadcast)
定义:顾名思义,就是有顺序的广播,但是要注意的是这里的有序是对广播接收器而言的。也就是说广播的发送并没有顺序,接收者可以设定优先级的高低。
特点:
1、广播接收器按照优先级的高低来收到广播,高优先级的接收者先收到广播;
2、中途可以中断广播的向下传递;
3、高优先级的receiver可以修改向下传递的广播的内容。
使用:
- 在
manifest
中的<receiver
中设定广播接收器的优先级,重要的是android:priority="100"
。
<intent-filter android:priority="100">
<action android:name="com.sky.intent.action.MYBROADCASTRECEIVER"/>
</intent-filter>
然后再定义一个MyBroadcastReceiver2,重写onReceive()
并设定优先级android:priority="99"
。
- 发送有序广播时使用以下方法:
public void sendMyOrderdBroadcast(View view){
Intent intent = new Intent(ACTION_MYBROADCAST);
// 第二个参数为receiverPermission
// 是一个字符串权限,设定后广播接收器注册该权限后才能收到
sendOrderedBroadcast(intent,null);
}
这样就可以弹出两次Toast信息。
- 拦截广播:在优先级较高的广播接收器MyBroadcastReceiver的
onReceive
方法中调用abortBroadcast()
方法。
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"MyBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
abortBroadcast();
}
就可以发现Toast只弹出一次,说明广播被拦截了。
- 修改向下传递的广播内容:
(1) MyBroadcastReceiver的onReceive
方法中接收信息,然后可以重新设置Bundle对象传递的信息。
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"MyBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
//获取处理的的广播,普通广播不能获取处理
//true代表如果前面的接收器没有存放数据,则自动创建一个空的Bundle对象
//false则表示如果前面的接收器如果没有存放任何数据则返回null。
Bundle bundle = getResultExtras(true);
bundle.putString("message","Modify Message");
setResultExtras(bundle);
}
(2) MyBroadcastReceiver2的onReceive
中直接获取信息。这样就完成了信息的修改传递。
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = getResultExtras(true);
Toast.makeText(context,"MyBroadcastReceiver2 Get Message"+bundle.get("message"),Toast.LENGTH_SHORT).show();
}
2.3 系统广播(System Broadcast)
定义:Android系统内置各种广播,当手机状态发生变化时系统会发送广播。监听系统广播然后可以进行逻辑处理。
使用:设置广播接收器intent-filter的不同action,可以接收到系统不同状态所发送的广播。
例如下方是监听系统网络变化接收广播,另外要注意申请相应权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
...
<receiver android:name=".MySysBroadcastReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
@Override
public void onReceive(Context context, Intent intent) {
//**判断当前的网络连接状态是否可用*/
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if ( info != null && info.isAvailable()){
//当前网络状态可用
Toast.makeText(context,"网络连接已恢复",Toast.LENGTH_SHORT).show();//封装的Toast
}else {
//当前网络不可用
Toast.makeText(context,"无网络连接",Toast.LENGTH_SHORT).show();//封装的Toast
}
}
不同的action对应不同的系统状态,列举如下:
系统操作 | action name |
---|---|
监听网络变化 | android.net.conn.CONNECTIVITY_CHANGE |
关闭或打开飞行模式 | android.intent.action.AIRPLANE_MODE |
充电时或电量发生变化 | android.intent.action.BATTERY_CHANGED |
电池电量低 | android.intent.action.BATTERY_LOW |
电池电量充满 | android.intent.action.BATTERY_OKAY |
系统启动完成后(开机完成) | android.intent.action.BOOT_COMPLETED |
按下拍照按键 | android.intent.action.CAMERA_BUTTON |
设备当前设置被改变时(界面语言、设备方向等) | android.intent.action.CONFIGURATION_CHANGED |
插入耳机时 | android.intent.action.HEADSET_PLUG |
未正确移除SD卡但已取出来时(正确移除方法:设置--SD卡和设备内存--卸载SD卡) | android.intent.action.MEDIA_BAD_REMOVAL |
插入外部储存装置(如SD卡) | android.intent.action.MEDIA_CHECKING |
成功安装APK | android.intent.action.PACKAGE_ADDED |
成功删除APK | android.intent.action.PACKAGE_REMOVED |
屏幕关闭 | android.intent.action.SCREEN_OFF |
屏幕打开 | android.intent.action.SCREEN_ON |
用户解锁 | android.intent.action.USER_UNLOCKED |
用户锁屏 | android.intent.action.USER_PRESENT |
重启设备 | android.intent.action.REBOOT |
关闭系统时 | android.intent.action.ACTION_SHUTDOWN |
2.4 本地广播(Local Broadcast)
定义:Android引入的一套本地广播机制,使用该机制发出的广播只能在应用程序内部进行传递,并且广播也只能是APP内部的BroadcastReceiver的接收。
优点:
- 高效率:本地广播的发送和接收都只在当前App进行。
- 安全性高:全局广播存在许多问题,比如携带数据的广播有可能被其它应用截获、其它应用发送垃圾广播影响自身应用运行,而本地广播则没有这些问题。
使用:
- 全局广播设置为本地广播:
- 注册广播时,将exported属性设置为false,即不予其它应用程序产生联系。
- 发送和接收广播时设置权限permission。
sendBroadcast(intent,"com.sky.broadcast_test.permission");
如果发送的broadcast带有permission,那么只有在那些manifest文件中包含了<uses-permission>的receiver才能够接受到;高版本需要设置权限级别。
<uses-permission android:name="com.sky.broadcast_test.permission"/>
<permission
android:name="com.sky.broadcast_test.permission"
android:protectionLevel="normal" />
如果在<receiver>中设置了<android:permission="">,那么只有在那些manifest文件中包含了<uses-permission>的broadcast才能够发送给它。
- 发送广播时指定广播接收器所在的包名。
intent.setPackage("packageName");
- 使用LocalBroadcastManager来发送本地广播。步骤:
- 自定义一个广播,这里以内部类的形式写到了MainActivity。
class MyLocalBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"MyLocalBroadcastReceiver Get Message",Toast.LENGTH_SHORT).show();
}
}
- 获取LocalBroadcastManager实例,创建自定义广播对象,动态注册广播。
// 获取LocalBroadcastManager实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 设置IntentFilter的action
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_MY_LOCAL_BROADCAST);
// 动态注册广播
myLocalBroadcastReceiver = new MyLocalBroadcastReceiver();
localBroadcastManager.registerReceiver(myLocalBroadcastReceiver,intentFilter);
- 使用localBroadcastManager发送广播。
public void sendMyLocalBroadcast(View view){
// 使用localBroadcastManager发送广播
Intent intent = new Intent(ACTION_MY_LOCAL_BROADCAST);
localBroadcastManager.sendBroadcast(intent);
}
- 解除注册,防止内存泄漏。
@Override
protected void onDestroy() {
super.onDestroy();
// 解除注册,预防内存泄漏
localBroadcastManager.unregisterReceiver(myLocalBroadcastReceiver);
}
2.5 粘性广播(Sticky Broadcast)
被废弃,Android5.0 & API 21已经失效,不用管它。
三. BroadcastReceiver两种注册方式
3.1 静态注册
即在清单文件manifest
中通过声明<receiver>
注册。<receiver
有很多属性,下面列表说明:
属性名称 | 作用 |
---|---|
android:name="" | 类名 |
android:enabled = [ true / false] | 是否可用 |
android:exported= [ true / false] | 此接收器是否接收其它应用广播,包含<intent-filter>标签默认为true |
android:icon="" | 图标 |
android:label="" | 标签名称 |
android:permission="" | 权限,具有该权限的发送者发送的广播才能被它接收 |
android:process="" | Android四大组件都可以通过这个属性指定独立进程 |
<intent-filter> | 过滤器 |
<intent-filter>中可以指定<action android:name=""/>
用来作发送和接收的标记。
3.2 动态注册
就是在代码中注册BroadcastReceiver,具体步骤举例:
// 1.实例化自定义广播,IntentFilter
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
// 2.设置IntentFilter的action
intentFilter.addAction("Your Action");
// 3.使用Context的registerReceiver动态注册广播
registerReceiver(myBroadcastReceiver,intentFilter);
...
// 4.解除注册,防止内存泄漏
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myBroadcastReceiver);
}
3.3 两种注册方式的区别
这里借用一张总结的比较好的图来说明:
两种注册方式的区别.png四. BroadcastReceiver原理
- Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。
因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展。
- 模型中有三个角色:
- 消息订阅者(BroadcastReceiver)
- 消息发布者(广播发布者)
- 消息中心(
AMS
,即Activity Manager Service
)
- 原理描述:
- 广播接收者(BroadcastReceiver) 通过
Binder
机制在AMS
注册 - 广播发送者 通过
Binder
机制向AMS
发送广播 -
AMS
根据 广播发送者 要求,在已注册列表中,寻找合适的广播接收者
寻找依据:
IntentFilter / Permission
-
AMS
将广播发送到合适的广播接收者相应的消息循环队列中; - 广播接收者通过 消息循环 拿到此广播,并回调
onReceive()
。
特别注意:广播发送者 和 广播接收者的执行 是 异步 的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到;
五. BroadcastReceiver回调Context
对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
- 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
- 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
- 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
- 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
测试项目地址。
参考资料:
网友评论