作为Android四大组件之一的BroadcastReceiver(广播接收者),同Activity(活动)一样,经常被大家用到,网上也是一堆对它的讲解,那么为什么Android要用广播接收者这种机制呢?
广播分为:普通广播和有序广播
1.Normal broadcasts(普通广播):Normal broadcasts是完全异步的可以同一时间被所有的接收者接收到。消息的传递效率比较高。但缺点是接收者不能将接收的消息的处理信息传递给下一个接收者也不能停止消息的传播。可以利用Context.sendBroadcast发送。
2.Ordered broadcasts(有序广播):Ordered broadcasts的接收者按照一定的优先级进行消息的接收。一次传送到一个接收器。 随着每个接收器依次执行,它可以将结果传播到下一个接收器,或者它可以完全中止广播,使得它不会被传递到其他接收器。 命令接收器运行可以用匹配的意图过滤器的android:priority
属性控制; 具有相同优先级的接收器将以任意顺序运行。可以利用Context.sendOrderedBroadcast发送。
官网上介绍广播是用的监听系统网络状况的例子,其实关键字在于“监听”。
(1) 创建广播接收者
BroadcastReceiver是一个抽象类,所以我们要创建自己的广播接收者就要继承它,继承后会有提示重写onReceive方法。
public class NetworkBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
if (activeNetwork != null && activeNetwork.isAvailable()) {
Toast.makeText(context, "有网络连接", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "无网络连接", Toast.LENGTH_SHORT).show();
}
}
}
}
广播接收者的生命周期是从接收广播开始,到onRecevier方法执行完成结束,时间很短,一般不允许处理大批量耗时操作。这里顺便给出打印NetworkInfo的信息以供参考:
NetworkInfo:
type: WIFI[,type_ext: WIFI],
state: CONNECTED/CONNECTED,
reason: (unspecified),
extra: "TP-LINK_EFE8",
roaming: false,
failover: false,
isAvailable: true,
isConnectedToProvisioningNetwork: false,
isIpv4Connected: true,
isIpv6Connected: false
[type: MOBILE[LTE],
state: CONNECTED/CONNECTED,
reason: connected,
extra: cmnet,
roaming: false,
failover: false,
isAvailable: true,
isConnectedToProvisioningNetwork: false]
(2) 静态注册广播
静态注册广播,需要在AndroidManifest.xml中,添加<recevier/> 标签,将广播接收者注册到应用中。要添加过滤器IntentFilter,由于系统网络变化时会发送ConnectivityManager.CONNECTIVITY_ACTION ("android.net.conn.CONNECTIVITY_CHANGE")的广播,所以我们要监听这条广播。
<receiver android:name=".NetworkBroadcastReceiver">
<intent-filter android:priority="1000">
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
这里priority代表的是执行顺序的优先级,取值[-1000,1000],后面的有序广播会讲到。
(3) 动态注册广播
i.意图过滤器 IntentFilter 用于给BroadcastReceiver绑定监听广播类型
ii.自定义的BroadcastReceiver,例如上文的NetworkChangeBroadcastReceiver
iii.注册方法 Context.registerReceiver(Receiver, IntentFilter)
iv.反注册方法 unregisterReceiver(Receiver)
IntentFilter mFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
NetworkChangeBroadcastReceiver mReceiver = new NetworkChangeBroadcastReceiver();
registerReceiver(mReceiver, mFilter);
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
这段代码是成对出现的,可以在onCreate的时候注册,在onDestroy的时候反注册,也可以在onResume和onPause中执行这写方法。不过Google API推荐的做法,在activity的onResume()中注册,在onPause()反注册。效果是当界面pause时,就不接收广播,从而减少不必要的系统开销。还有就是一定要主动反注册你的广播,否则会出现异常。
动态注册和静态注册的差别:动态注册后,广播接收者会依赖Activity的生命周期,而静态注册的广播不会,只要是系统有发出的广播,它都会接收,与程序是否启动无关。
(4) 发送普通广播
具体使用的方法是sendBroadcast(Intent intent),通过隐式调用就可以,注意action是你自定义的,意思就是不可以发送系统广播,我试了,直接就崩了。
Intent intent = new Intent();
intent.setAction("com.fleming.chen.mybroadcast");
sendBroadcast(intent);
针对(3)(4)两点,如果你要用到的广播仅仅是应用里的,那么你可以用LocalBroadcastManager这个类,它与上述描述中的区别在于:
LocalBroadcastManager.getInstance(context).registerReceiver(mReceiver, mFilter);
LocalBroadcastManager.getInstance(context).unregisterReceiver(mReceiver);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
通过sendBroadcast发送的广播,不会被通过LocalBroadcastManager类注册的广播接收者接收,反之也是如此,两者是不可以”互通友谊“的,推荐使用LocalBroadcastManager来管理广播。
(5) 发送有序广播
上面讲了那么多都是普通广播,那什么又是有序广播呢?
有序广播关键在于这类广播是有序的,上文中提到priority,这是IntentFilter的属性,用来让不同的广播拥有不同的执行顺序,即优先级不同。
定义三种不同优先级的广播接收者:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.fleming.chen.myreceiver")) {
String message = getResultData();
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
setResultData("这是修改后的数据");//第一个接收后处理一下,再交给下一个
}
}
}
public class MyBroadcastReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.fleming.chen.myreceiver")) {
String message = getResultData();//得到上一个的处理结果
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
abortBroadcast();//主动停止广播,不再继续传下去
}
}
}
public class MyBroadcastReceiver3 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.fleming.chen.myreceiver")) {
//此时虽然该广播接收者也监听了,不过也没有内容
Toast.makeText(context, getResultData(), Toast.LENGTH_SHORT).show();
}
}
}
<receiver android:name=".MyBroadcastReceiver" >
<intent-filter android:priority="1000">
<action android:name="com.fleming.chen.myreceiver"/>
</intent-filter>
</receiver>
<receiver android:name=".MyBroadcastReceiver2">
<intent-filter android:priority="0">
<action android:name="com.fleming.chen.myreceiver"/>
</intent-filter>
</receiver>
<receiver android:name=".MyBroadcastReceiver3">
<intent-filter android:priority="-1000">
<action android:name="com.fleming.chen.myreceiver"/>
</intent-filter>
</receiver>
Intent intent = new Intent();
intent.setAction("com.fleming.chen.myreceiver");
sendOrderedBroadcast(intent, null, null, null, 0, "这是初始的数据", null);
对于广播的内容,在Android 7.0上做了修改,即Project Svelte:后台优化
Android 7.0 移除了三项隐式广播,以帮助优化内存使用和电量消耗。此项变更很有必要,因为隐式广播会在后台频繁启动已注册侦听这些广播的应用。删除这些广播可以显著提升设备性能和用户体验。
移动设备会经历频繁的连接变更,例如在 WLAN 和移动数据之间切换时。目前,可以通过在应用清单中注册一个接收器来侦听隐式 CONNECTIVITY_ACTION
广播,让应用能够监控这些变更。由于很多应用会注册接收此广播,因此单次网络切换即会导致所有应用被唤醒并同时处理此广播。
同理,在之前版本的 Android 中,应用可以注册接收来自其他应用(例如相机)的隐式 ACTION_NEW_PICTURE
和 ACTION_NEW_VIDEO
广播。当用户使用相机应用拍摄照片时,这些应用即会被唤醒以处理广播。
为缓解这些问题,Android 7.0 应用了以下优化措施:
- 面向 Android 7.0 开发的应用不会收到
CONNECTIVITY_ACTION
广播,即使它们已有清单条目来请求接受这些事件的通知。在前台运行的应用如果使用BroadcastReceiver
请求接收通知,则仍可以在主线程中侦听CONNECTIVITY_CHANGE
。 - 应用无法发送或接收
ACTION_NEW_PICTURE
或ACTION_NEW_VIDEO
广播。此项优化会影响所有应用,而不仅仅是面向 Android 7.0 的应用。
如果您的应用使用任何 intent,您仍需要尽快移除它们的依赖关系,以正确适配 Android 7.0 设备。Android 框架提供多个解决方案来缓解对这些隐式广播的需求。例如,JobScheduler
API 提供了一个稳健可靠的机制来安排满足指定条件(例如连入无限流量网络)时所执行的网络操作。您甚至可以使用 JobScheduler
来适应内容提供程序变化。
所以说,在Android的世界,到处都充满着广播,就是为了用来监听手机的各种状态,给用户提醒,这是一种很好的用户体验,不过任何事情都是如此,广播也不可以多用哦,另外附上项目地址:https://github.com/flemingme/NetworkChange 供参考。