1.简介
BroadcastReceiver 运用在应用程序组件与组件之间进行通信,可以跨应用程序传递,发送广播内容是一个Intent,这个Intent中可以携带我们要发送的数据。
广播有两个角色,一个是广播发送者,另外一个是广播接收者.
2.使用场景
- 同一app内有多个进程的不同组件之间的消息通信。
- 不同app之间的组件之间消息的通信。
3.广播种类
- 标准广播:context.sendBroadcast(Intent)方法发送的广播,无序,不可被拦截
- 有序广播:context.sendOrderBroadcast(Intent)方法发送的广播,可被拦截
- 本地广播:localBroadcastManager.sendBroadcast(Intent),只在app内传播
4.两个注册方式
图解5.1 使用(标准广播)
-
静态注册:在manifest.xml文件中注册
先创建一个广播接接收类 继承 BroadcastReceiver类
示例:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接收到广播", Toast.LENGTH_LONG).show();
}
}
静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用
注意: android7.0 以后android系统取消了一些系统广播,列:网络,拍照,录像
为了适配7.0以后最好使用动态广播
<!-- 申请权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 静态广播 为了适配7.0以后最好使用动态广播 -->
<receiver
android:name=".broadcastReceiver.MyBroadcastReceiver"
//属性表示接收者对外部应用程序不可用,即不接受来自外部的广播
android:exported="false"
>
<intent-filter >
<!-- 用于接收网络状态改变时发出的广播 由于7.0后取消了网络,拍照,录像...静态广播 -->
<!-- 为了适配所以最好使用动态广播-->
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<!-- 自定义广播 -->
<action android:name="customBroadcast"/>
</intent-filter>
</receiver>
-
动态注册:
动态注册的广播接收器可以自由的控制注册与注销, 在灵活性方面有很大的优势, 但是它必须在程序启动之后才能接收到广播, 因为注册的逻辑写在onCreate()方法中. 那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢? 这就需要使用静态注册的方式了.
在activity中通过registerReceiver()注册广播,注册广播需要一个IntentFilter,一个广播接收者BroadcastReceiver ,所以创建一个类继承BroadcastReceiver类并重写父类的onReceive()方法就行了.
示例: 这里加了一个自定义广播和一个监听网络状态的系统广播
public class MyBroadcastReceiver extends BroadcastReceiver {
// 自定义广播
public static final String CUSTOMBROADCAST = "customBroadcast";
public static final String CONNECTIVITY_CHANGE = "android.net.conn.CONNECTIVITY_CHANGE";
// 网络状态码
public static int NetStatus = -1; // -1:网络异常 0:wifi 1:3G
NetChangeStatusLinstener statusLinstener;
public MyBroadcastReceiver() {
}
public MyBroadcastReceiver(NetChangeStatusLinstener statusLinstener) {
this.statusLinstener= statusLinstener;
}
@Override
public void onReceive(Context context, Intent intent) {
// 自定义广播
if (CUSTOMBROADCAST.equals(intent.getAction())) {
String msg = intent.getStringExtra("msg");
ToastUitl.showShort(msg);
} else if (CONNECTIVITY_CHANGE.equals(intent.getAction())) { // 用于接收网络状态改变时发出的广播 系统广播
if (NetWorkUtils.isNetConnected(context)) {
if (NetWorkUtils.isWifiConnected(context)) {
// ToastUitl.showShort("当前是wifi网络状态!!!");
NetStatus = 0;
setNetStatus(NetStatus);
return;
}
if (NetWorkUtils.is3gConnected(context)) {
// ToastUitl.showShort("当前3G网络状态");
NetStatus = 1;
setNetStatus(NetStatus);
}
} else {
// ToastUitl.showShort("网络异常,请检查网络是否异常");
setNetStatus(NetStatus);
}
} else {
LogUtils.logd("以上条件不满足的广播!!");
}
}
public void setNetStatus(int status){
if (statusLinstener!=null) {
statusLinstener.onNetStatusLinstener(status);
}
}
// 监听网络状态的回调接口
public interface NetChangeStatusLinstener {
void onNetStatusLinstener(int status);
}
}
activity 注册: 并发送广播
public class BroadcastActivity extends BaseActivity {
private MyBroadcastReceiver myBroadcastReceiver;
private XWUIRoundButton sendBroadcast;
// 实现网络监听接口
MyBroadcastReceiver.NetChangeStatusLinstener netChangeStatusLinstener
= new MyBroadcastReceiver.NetChangeStatusLinstener() {
@Override
public void onNetStatusLinstener(int status) {
switch (status) {
case -1:
ToastUitl.showShort("网络异常,请检查网络是否异常");
break;
case 0:
ToastUitl.showShort("当前是wifi网络状态!!!");
break;
case 1:
ToastUitl.showShort("当前3G网络状态");
break;
}
}
};
@Override
protected int initContentView() {
return R.layout.activity_static_broadcast;
}
@Override
protected void initData() {
}
@Override
protected void initView(Bundle savedInstanceState) {
sendBroadcast = findViewById(R.id.btn_sendBroadcast);
setToolBarTitle("广播接收者");
}
@Override
protected void onResume() {
super.onResume();
// 意图广播
IntentFilter intentFilter = new IntentFilter(); //
// 可以添加多个意图
intentFilter.addAction(CONNECTIVITY_CHANGE); // 用于接收网络状态改变时发出的广播 属于系统广播
// intentFilter.addAction(CUSTOMBROADCAST); // 自定义广播
myBroadcastReceiver = new MyBroadcastReceiver(netChangeStatusLinstener);
// 注册广播
registerReceiver(myBroadcastReceiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
if (myBroadcastReceiver != null) {
// 注销广播
unregisterReceiver(myBroadcastReceiver);
}
}
// 这是点击按钮发送自定义广播
public void sendBroadcast(View view) {
// sendBroadcast方法发送广播
Intent intent = new Intent();
// 设置发什么广播 这里填的参数必输是被注册过的广播
intent.setAction(CUSTOMBROADCAST);
intent.putExtra("msg", "自定义广播发送成功!!!");
//适配8.0系统静态注册的接收不到消息 参数1:是包名 参数2 : 广播接收者的类
intent.setComponent(new ComponentName(this,MyBroadcastReceiver.class));
sendBroadcast(intent);
}
}
注意:
- 静态注册和动态注册不能出现一样Action 不然onReceive()方法会执行两次
- 注:动态广播最好在Activity 的 onResume()注册、onPause()注销。
- 原因:对于动态广播,有注册就必然得有注销,否则会导致内存泄露
在onResume()注册、onPause()注销是因为onPause()在App死亡前一定会被执行,从而保证广播在App死亡前一定会被注销,从而防止内存泄露。
- 不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:
当系统因为内存不足(优先级更高的应用需要内存)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。- 假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。
- 但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。
-
Android系统广播action如下:
5.2 有序广播
有序广播跟标准广播使用基本一样.
有序广播接受者接收广播的顺序规则是通过Priority(优先级)属性值从大-小排序(1000到-1000)执行,Priority属性相同者,动态注册的广播优先;
有序广播是通过sendOrderedBroadcast()发送广播.
示例:
<receiver android:name=".broadcastReceiver.MyBroadcastReceiver">
<intent-filter android:priority="500">
<!-- 自定义广播 -->
<action android:name="customBroadcast" />
</intent-filter>
</receiver>
<receiver
android:name=".broadcastReceiver.MyBroadcastReceiverTwo"
>
<intent-filter android:priority="900">
<!-- 自定义广播 -->
<action android:name="customBroadcast" />
</intent-filter>
</receiver>
//这里通过按钮点击发送广播
public void sendBroadcast(View view) {
Intent intent = new Intent();
// 设置发什么广播 这里填的参数必输是被注册过的广播
intent.setAction(CUSTOMBROADCAST);
intent.putExtra("msg", "自定义广播发送成功!!!");
sendOrderedBroadcast(intent,null);
}
// 广播接收者1
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
LogUtils.logd(msg+" : One");
}
}
// 广播接收者2
public class MyBroadcastReceiverTwo extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
LogUtils.logd(msg+" : Two");
}
}
最后输出的是
自定义广播发送成功!!! : Two
自定义广播发送成功!!! : One
5.3 本地广播
本地广播和全局广播的区别
- 本地广播:发送的广播事件不被其他应用程序获取,也不能响应其他应用程序发送的广播事件。只在本app中有效,因此不必担心泄漏隐私的数据。本地广播只能被动态注册,不能静态注册。动态注册或方法时需要用到LocalBroadcastManager. 不接收系统广播,更高效.()
- 全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件(可以通过 exported–是否监听其他应用程序发送的广播 在清单文件中控制) 全局广播既可以动态注册,也可以静态注册。
内部实现机制:
- LocalBroadcast高效的原因:因为它内部是通过Handler实现的,它的sendBroadcast()方法含义并非和系统的sendBroadcast()一样,它的sendBroadcast()方法其实就是通过Handler发送了一个Message而已。
- LocalBroadcast安全的原因:既然它是通过Handler实现广播发送的,那么相比系统广播通过Binder机制实现那肯定更加高效,同时使用Handler来实现,别的app无法向我们应用发送该广播,而我们app内部发送的广播也不会离开我们的app。
- LocalBroadcast内部协作主要是靠两个Map集合:mReceivers和mActions,当然还有一个List集合mPendingBroadcasts,这个主要存储待接收的广播对象。
基本使用
@Override
protected void onResume() {
super.onResume();
// 本地广播
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 意图广播
IntentFilter intentFilter = new IntentFilter(); //
// 可以添加多个意图 // 自定义广播
intentFilter.addAction(CUSTOMBROADCAST);
myBroadcastReceiver = new MyBroadcastReceiver(netChangeStatusLinstener);
// 注册本地广播
localBroadcastManager.registerReceiver(myBroadcastReceiver, intentFilter);
}
// 点击按钮发送广播
public void sendBroadcast(View view) {
// sendBroadcast方法发送广播
Intent intent = new Intent();
// 设置发什么广播 这里填的参数必输是被注册过的广播
intent.setAction(CUSTOMBROADCAST);
intent.putExtra("msg", "自定义广播发送成功!!!");
sendBroadcast(intent);
localBroadcastManager.sendBroadcast(intent);
}
@Override
protected void onPause() {
super.onPause();
if (localBroadcastManager != null && myBroadcastReceiver!=null) {
// 注销广播
localBroadcastManager.unregisterReceiver(myBroadcastReceiver);
}
}
网友评论