参考:Android 四大组件
BroadcastReceiver 简介
广播的生命周期很短(调用对象–>回调 onReceive –>结束),广播对象也是在应用进程的主线程中被构造,不适合耗时的操作,也不推荐开启新线程,因为往往线程还没结束,广播对象就已经执行完毕而被系统销毁了。每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应。
BroadcastReceiver 注册
注册广播的方式有两种,在代码中注册和在清单文件中注册,前者称为动态注册,后者称为静态注册。
使用步骤:
自定义一个类继承 BroadcastReceiver
重写 onReceive 方法
注册广播
不再使用时,取消注册广播
动态注册方式
案例使用(监听网络状态变化):
第一和第二步:
public class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "network change", Toast.LENGTH_LONG).show();
}
}
第三步:
mNetworkChangeReceiver = new NetworkChangeReceiver();
//动态注册:创建一个IntentFilter的实例,添加网络变化的广播(功能是对组件进行过滤,只获取需要的消息)
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(mNetworkChangeReceiver, intentFilter);
第四步:
unregisterReceiver(netWorkChangeReceiver);
静态注册方式
动态注册的方式比较灵活,但缺点是:必须在程序启动之后才能接收到广播,因为注册的逻辑是通过代码方式实现的。为了让程序在未启动的情况下就能接收到广播,这里就需要使用到静态注册。
案例使用:
第一和第二步:
public class TestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//测试 10s 内没执行完,会不会造成 ANR
// for (int i = 0; i < Integer.MAX_VALUE; i++) {
// i--;
// }
Toast.makeText(context, "TestReceiver", Toast.LENGTH_LONG).show();
}
}
第三步:
<receiver android:name=".receiver.TestReceiver">
<intent-filter>
<action android:name="zeng.fanda.com.fourmoduledemo.test"/>
</intent-filter>
</receiver>
注意: Android 8.0 的广播对静态注册做了限制,需要用动态注册的方式,不然会报 Background execution not allowed(不允许后台执行)的日志信息,广播也无法正常工作。
BroadcastReceiver 发送
广播被分为两种不同的类型:普通广播(无序广播或标准广播)和有序广播。
特点
- 无序广播:
完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高。
接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播。
- 有序广播:
- 有序广播是按照接收者声明的优先级别来传递的,被接收者依次接收广播。接收者可以拦截广播来终止向下传播,也可以存入数据来传递给下一个接收者,即下一个广播接收者可以获取到上一个广播存入的数据。
发送无序广播
//广播一
public class TestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Logger.i("TestReceiver-1");
}
}
//广播二
public class TestReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Logger.i("TestReceiver-2");
}
}
//同时注册相同的过滤信息
<receiver android:name=".receiver.TestReceiver">
<intent-filter>
<action android:name="zeng.fanda.com.fourmoduledemo.test" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.TestReceiver2">
<intent-filter>
<action android:name="zeng.fanda.com.fourmoduledemo.test" />
</intent-filter>
</receiver>
//发送广播
sendBroadcast(new Intent("zeng.fanda.com.fourmoduledemo.test"));
结果如下:
发送有序广播
//广播一
public class TestReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//拦截后终止广播的传递
abortBroadcast();
Logger.i("TestReceiver-1");
}
}
//广播二
public class TestReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Logger.i("TestReceiver-2");
}
}
//同时注册相同的过滤信息
<receiver android:name=".receiver.TestReceiver">
// 设置广播优化级为1000
<intent-filter android:priority="1000">
<action android:name="zeng.fanda.com.fourmoduledemo.test" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.TestReceiver2">
// 设置广播优化级为500
<intent-filter android:priority="500">
<action android:name="zeng.fanda.com.fourmoduledemo.test" />
</intent-filter>
</receiver>
//第二个参数是一个与权限相关的字符串,这里传入null即可
sendOrderedBroadcast(new Intent("zeng.fanda.com.fourmoduledemo.test"),null);
结果如下:
由于广播一的优先级大于广播二,所以广播一先收到广播,并且广播一调用了 abortBroadcast() 方法来终止了广播的传递,所以广播二是收不到广播的,自然就不打印日志了。
上述通过 android:priority 属性给广播接收器设置了优先级。这个属性的范围在 -1000 到 1000 ,数值越大,优先级越高。也可以动态地在代码调用 IntentFilter 对象的 setPriority() 进行设置。
我们也可以在广播一中通过 setResultXXX 方法存入一些数据,在广播二中通过 getResultXXX 方法获取到广播一中的数据。如果还有广播三四五等,都可以获取到上一个广播通过 setResultXXX 方法存入的数据,比如广播三比广播二优化级低,广播二也设置过数据,则广播三只能拿到广播二存入的数据。
本地广播
之前我们发送和接收的广播全部都属于全局广播,即发出去的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样一来,必然会造成安全问题。于是便有了本地广播:即只能在本应用程序中发送和接收广播。只用使用 LocalBroadcastManager 类对广播进行收发就可以了。
//获取实例
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
//注册
mLocalBroadcastManager.registerReceiver(mNetworkChangeReceiver,intentFilter);
//发送
mLocalBroadcastManager.sendBroadcast(new Intent("android.net.conn.CONNECTIVITY_CHANGE"));
//反注册
mLocalBroadcastManager.unregisterReceiver(mNetworkChangeReceiver);
注意:本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播。而发送本地广播时,我们的程序肯定已经启动了,没有必要使用到静态注册的功能。
静态注册和动态注册区别
动态注册广播不是常驻型广播,也就是说广播跟随 activity 的生命周期。注意: 在 activity 结束前,移除广播接收器。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。当广播为有序广播时:
- 优先级高的先接收
- 同优先级的广播接收器,动态优先于静态
- 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
当广播为普通广播时:
- 无视优先级,动态广播接收器优先于静态广播接收器
- 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。