Android之 BroadcastReceiver广播接收器详解
broadcast receiver广播接收器详解
一 四大组件
1.1 Activity组件,它一个单独的窗口,程序流程都必须在Activity中运行。
1.2 service组件,用于在后台完成用户指定的操作。
1.3 content provider组件,会为所有的应用准备一个内容窗口,并且保留数据库、文件。
1.4 broadcast receiver组件,是程序之间传递信息时的一种机制,作用就是接收或者发送通知。
二 broadcast receiver简介
2.1 broadcast receiver做为四大组件之一,负责组件之间消息的传递和接收。
2.2 采用观察者模式,基于消息的发布/订阅事件模型,通过Binder机制进行消息的注册和接收。
2.3 广播中的角色:
- 消息订阅者(广播接收者)
- 消息发布者(广播发布者)
- 消息中心(AMS,即Activity Manager Service)
2.4 广播的工作流程:
- 广播接收者通过 Binder机制在AMS注册
- 广播发送者通过 Binder 机制向AMS发送广播
- AMS根据广播发送者要求,在已注册列表中,寻找合适的广播接收者(寻找依据:IntentFilter / Permission)
- AMS将广播发送到合适的广播接收者相应的消息循环队列中;
- 广播接收者通过消息循环拿到此广播,并回调 onReceive()
三 广播的注册
3.1 注册类型有两种:静态注册和动态注册
3.2 静态注册,即在清单文件中注册
第一步:创建一个广播接收器BroadcastReceiver ,广播也是通过Intent来传递数据。
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String msg=intent.getStringExtra("msg");
Log.e(TAG, "onReceive: "+msg);
}
}
第二步,清单文件注册该广播
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<receiver android:name=".receiver.MyReceiver"
android:exported="true"/>
</application>
第三步:在其它组件里面发送广播,比如Activity里面
private void buttonClick(){
Intent intent = new Intent(this, MyReceiver.class);
sendBroadcast(intent);
}
3.3 注意:
android 8.0以后版本对静态注册的广播做了限制,自定义的接收器会接收不到发送方发送的广播。发送方需要在intent中设定接收方的package,接收方才会接收到。如下:
Intent intent = new Intent();
intent.setAction("cn.dinghe.test");
intent.setClass(this,Class.forName("cn.dinghe.test.MyReceiver"));
sendBroadcast(intent);
3.4 动态注册:即通过 registerReceiver 注册
第一步:一样先创建广播接收器
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "MyReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String msg=intent.getStringExtra("msg");
Log.e(TAG, "onReceive: "+msg);
}
}
第二步:在组件如Activity里面动态注册广播
public class MainActivity extends AppCompatActivity {
MyReceiver myReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册广播
myReceiver=new MyReceiver();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("com.dinghe.test.myAction");
registerReceiver(myReceiver,intentFilter);
}
}
第三步:在任意组件里面发送广播
private void buttonClick(){
Intent intent=new Intent();
intent.setAction("com.dinghe.test");
intent.putExtra("mag","你好啊");
sendBroadcast(intent);
}
第四步:取消注册,不然容易造成内存泄漏
@Override
protected void onDestroy() {
super.onDestroy();
if (myReceiver!=null){
unregisterReceiver(myReceiver);
}
}
五 有序广播
5.1 有序广播介绍:
- 有序广播是一种同步执行的广播,广播发出之后,优先级高的广播接收器就可以先接收到广播消息。
- 执行完该广播接收器的逻辑后,可以选择截断正在传递的广播或者继续传递,如果广播消息被截断,之后的广播接收器则无法收到广播消息。
- 有序广播中的“有序”是针对广播接收者而言的。有序广播的定义过程与普通广播无异,只是其发送方式变为:sendOrderedBroadcast()。
- 有序广播的接收者们将按照事先命的优先级依次接收,数越大优先级越高(取值范围:-1000~10000)
- 优先级可以声明在<intent-filter android:priority="100"/>
- 也可以调用IntentFilter对象的 setPriority() 设置
- 调用abortBroadcast()方法即可终止,一旦终止后面接收者就无法接受广播
5.2 实战:
第一步,创建有序广播接收器
public class MyOrderBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyOrderBroadcastReceive";
@Override
public void onReceive(Context context, Intent intent) {
String action=intent.getAction();
String msg=intent.getStringExtra("msg");
switch (action){
case "BROADCAST_ACTION1":
break;
case "BROADCAST_ACTION2":
break;
}
}
}
第二步:清单文件里面注册有序广播 ,并设置优先级
<receiver
android:name=".MyOrderBroadcastReceiver" >
<!-- priority优先级:数字越高优先级越高 -->
<intent-filter android:priority="2">
<action android:name="BROADCAST_ACTION2" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="BROADCAST_ACTION1" />
</intent-filter>
</receiver>
或者动态注册并设置广播优先级
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("BROADCAST_ACTION2");
intentFilter.setPriority(2);
registerReceiver(mBroadcastReceiver, intentFilter);
第三步:发送广播
Intent intent = new Intent();
intent.setAction("BROADCAST_ACTION2");
intent.putExtra("msg","你好啊");
sendOrderedBroadcast(intent, null);
第四步:拦截有序广播,在接收器里面拦截abortBroadcast();
public class MyOrderBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "MyOrderBroadcastReceive";
@Override
public void onReceive(Context context, Intent intent) {
String action=intent.getAction();
String msg=intent.getStringExtra("msg");
switch (action){
case "BROADCAST_ACTION1":
break;
case "BROADCAST_ACTION2":
//优先级高,先收到消息,可以拦截断开有序广播,不再执行下一广播
abortBroadcast();
break;
}
}
}
六 应用内广播(LocalBroadcast)
6.1 Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:
- 其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
- 其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
6.2 解决款进程上面隐患方案如下:
- 对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
- 在广播发送和接收时,都增加上相应的permission,用于权限验证;
- 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
6.3 LocalBroadcastManage处理App内广播
Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。使用方式如下:
//registerReceiver(mBroadcastReceiver, intentFilter);
//注册应用内广播接收器
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//unregisterReceiver(mBroadcastReceiver);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
//sendBroadcast(intent);
//发送应用内广播
localBroadcastManager.sendBroadcast(intent);
七 常用系统广播
7.1 Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播。
7.2 当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播
7.3 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
监听网络变化
android.net.conn.CONNECTIVITY_CHANGE关闭或打开飞行模式
Intent.ACTION_AIRPLANE_MODE_CHANGED充电时或电量发生变化
Intent.ACTION_BATTERY_CHANGED电池电量低
Intent.ACTION_BATTERY_LOW电池电量
Intent.ACTION_BATTERY_OKAY系统启动完成后(仅广播一次)
Intent.ACTION_BOOT_COMPLETED按下照相时的拍照按键(硬件按键)时
Intent.ACTION_CAMERA_BUTTON屏幕锁屏
Intent.ACTION_CLOSE_SYSTEM_DIALOGS设备当前设置被改变时(界面语言、设备方向等)
Intent.ACTION_CONFIGURATION_CHANGED插入耳机时
Intent.ACTION_HEADSET_PLUG未正确移除SD卡但已取出来时(正确移除方法:设置--SD卡和设备内存--卸载SD卡)
Intent.ACTION_MEDIA_BAD_REMOVAL插入外部储存装置(如SD卡)
Intent.ACTION_MEDIA_CHECKING成功安装APK
Intent.ACTION_PACKAGE_ADDED成功删除APK
Intent.ACTION_PACKAGE_REMOVED重启设备
Intent.ACTION_REBOOT屏幕被关闭
Intent.ACTION_SCREEN_OFF屏幕被打开
Intent.ACTION_SCREEN_ON关闭系统时
Intent.ACTION_SHUTDOWN重启设备
Intent.ACTION_REBOOT
7.4 示例,比如监听事件分钟变化
第一步:创建时间变化监听器
/**
* 时间更新监听
*/
BroadcastReceiver mTimeUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
String action = intent.getAction();
if (action == null || action.isEmpty()) {
return;
}
if (action.equals(Intent.ACTION_TIME_TICK)) {
//system every 1 min send broadcast
Log.e(TAG, "onReceive: " + Config.FOOTPRINTTIME);
}
}
};
第二步:注册系统监听
/**
* 注册时间监听
*/
private void registerUpdateTimeReceiver() {
//register time update
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
registerReceiver(mTimeUpdateReceiver, filter);
}
第三步:取消系统时间监听
/**
* 注销时间监听
*/
private void unRegisterUpdateTimeReceiver() {
if (mTimeUpdateReceiver != null) {
unregisterReceiver(mTimeUpdateReceiver);
}
}
八 广播的其它方式
8.1 广播的作用就是会主动监听并接收数据变化,但广播是系统组件,需要传上下文,使用不规范可能会造成内存泄漏。而且系统的更新可能会限制广播的某些功能使用,于是也出现了基于发布/订阅模式的其它框架,比如使用非常广泛的EventBus。
8.2 EventBus 优点:
- 调度灵活,不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。
- 父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要。
- 通知的优先级,能够保证 Subscriber 关注最重要的通知。
- 粘滞事件 (sticky events) 能够保证通知不会因 Subscriber 的不在场而忽略
8.3 当然使用广播和EventBus都能达到监听消息的机制,可以根据自己情况选择。还有其它类似的发送/接收机制的框架,比如配合网络请求使用的RxJava,LiveData等。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)