BroadcastReceiver的总结
前言
中文名为广播接收者,根据名字其实大家可以知道它的作用,就像学校安装在教室的播放器一样,起到一个接收通知的作用。针对这一范围的所有播放器。那么网络中的广播可以理解为针对这个局域网里的所有用户发送广播。(结合大佬们的博客总结的,如有侵权,麻烦联系我删除此文章)
广播
在了解广播接收者之前,我们先来了解一下广播。
广播的类型
-
标准广播(Normal broadcasts):是一种完全异步执行的广播,在广播发出后,所有的广播接收器几乎都会在同一时间接收到这条广播,没有先后顺序。如图:
NormalBroadcasr.PNG -
有序广播(Ordered broadcasts):是一种同步执行的广播,在广播发出后,同一时刻只有一个广播接收器能接收到广播。有先后顺序。如图:
OrderedBroadcast.PNG
普通广播的广播接收器是并行无序执行的,有序广播的广播接收器按照广播优先级串行执行
还可以分为:
系统广播
系统中内置了很多广播,如电量变化,网络变化,开关机状态等等。
举个栗子
这里我们写个栗子,比如监听系统广播的网络变化:
第一步:新建一个类,继承BroadcastReceiver,并重写onReceiver()方法
public class NetBraodcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
LogUtil.log("net is change");
ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
if (info != null && info.isAvailable()) {
LogUtil.log("net is Available");
}
else{
LogUtil.log("net isn't Available");
}
}
}
第二步:注册监听器(分为动态注册和静态注册),这里使用的是动态注册,在Activity中
private void init() {
LogUtil.log("注册广播");
IntentFilter filter = new IntentFilter();
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
netBraodcastReceiver = new NetBraodcastReceiver();
registerReceiver(netBraodcastReceiver,filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
LogUtil.log("注销广播");
unregisterReceiver(netBraodcastReceiver);
}
第三步:手动关闭开启网络查看运行结果
2019-06-23 13:51:12.336 30731-30731/sayhallo.cn.ilikeandroid E/LOG_: 注册广播
2019-06-23 13:51:12.356 30731-30731/sayhallo.cn.ilikeandroid E/LOG_: net is change
2019-06-23 13:51:12.357 30731-30731/sayhallo.cn.ilikeandroid E/LOG_: net isn't Available
2019-06-23 13:51:18.796 30731-30731/sayhallo.cn.ilikeandroid E/LOG_: net is change
2019-06-23 13:51:18.796 30731-30731/sayhallo.cn.ilikeandroid E/LOG_: net is Available
2019-06-23 13:51:25.958 30731-30731/sayhallo.cn.ilikeandroid E/LOG_: 注销广播
附加说明
- 记得添加网络权限
- 注册的方式:
1、静态注册:即在AndroidManifest.xml添加,如:
<receiver
android:name=".broadcast_receiver.PushBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.schoolpartime.chat.BROADCAST"/>
</intent-filter>
</receiver>
2、动态注册:即在代码中注册,如:
IntentFilter filter = new IntentFilter();
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
netBraodcastReceiver = new NetBraodcastReceiver();
registerReceiver(netBraodcastReceiver,filter);
并且,静态注册是广播接收者是全局监听的,而静态注册的广播接收者必须等到注册之后才能监听广播。
-
onReceive中不能执行耗时操作,如果耗时超过10s会弹出ANR。
-
onReceive中context参数,如果是静态注册的广播,context为ReceiverRestrictedContext,所在如果在这里要启动一个Activity的话(调用startActivity),需要在intent中添加Intent.FLAG_ACTIVITY_NEW_TASK;如果是动态注册的广播,context为当前注册时所在的组件,比如Activity或者Service。
-
这仅仅是系统广播中的一个,我们列举一下常用的系统广播:
字段 | 说明 |
---|---|
Intent.ACTION_AIRPLANE_MODE_CHANGED | 关闭或打开飞行模式时的广播 |
Intent.ACTION_BATTERY_CHANGED | 充电状态,或者电池的电量发生变化//电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册 |
Intent.ACTION_BATTERY_LOW | 表示电池电量低 |
Intent.ACTION_BATTERY_OKAY | 表示电池电量充足,即从电池电量低变化到饱满时会发出广播 |
Intent.ACTION_BOOT_COMPLETED | 在系统启动完成后,这个动作被广播一次(只有一次) |
Intent.ACTION_DATE_CHANGED | 设备日期发生改变时会发出此广播 |
Intent.ACTION_HEADSET_PLUG | 在耳机口上插入耳机时发出的广播 |
Intent.ACTION_INPUT_METHOD_CHANGED | 改变输入法时发出的广播 |
Intent.ACTION_LOCALE_CHANGED | 设备当前区域设置已更改时发出的广播 |
自定义广播
会用系统广播了,但是,很多时候我们需要自己去发送广播并接收,这个时候就需要自定义广播了。自己发送自己定义的广播。
举个栗子
这里我们写个栗子,比如发送字符串自己接收:
第一步:新建一个类,继承BroadcastReceiver,并重写onReceiver()方法
public class MyBraodcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra("data");
LogUtil.log("收到广播:" + data);
}
}
第二步:静态注册
<receiver android:name=".component.broadcastReceiver.MyBraodcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="sayhallo.cn.ilikeandroid.MyBraodcastReceiver"/>
</intent-filter>
</receiver>
第三步:发送广播并查看结果
LogUtil.log("发送广播");
Intent braodcast = new Intent("sayhallo.cn.ilikeandroid.MY_BROADCAST");
braodcast.setComponent(new ComponentName("sayhallo.cn.ilikeandroid","sayhallo.cn.ilikeandroid.component.broadcastRec eiver.MyBraodcastReceiver"));
braodcast.putExtra("data","这是我发送的广播");
sendBroadcast(braodcast);
运行结果:
2019-06-23 14:27:32.509 12032-12032/sayhallo.cn.ilikeandroid E/LOG_: 发送广播
2019-06-23 14:27:32.535 12032-12032/sayhallo.cn.ilikeandroid E/LOG_: 收到广播
2019-06-23 14:27:32.536 12032-12032/sayhallo.cn.ilikeandroid E/LOG_: 收到数据为:这是我发送的广播
附加说明
- Android8中使用静态注册的广播接收器接收自定义广播时,会发生接收不到自定义广播的问题,需要在Android7的代码基础上在发送广播的intent中加上这一句:
//ComponentName的第一个参数是自定义广播的包名,第二个参数是广播接收器的类
braodcast.setComponent(new ComponentName("pkgName","ReceiverName"));
- 之前发送的都是全局的,也就是所有的APP都能收到我们发送的广播,那么可不可以只发送给自己的APP去接收呢?答案是可以的,我们只需要新建一个LocalBroadcastManager对象(本地广播),然后再进行广播的操作即可。如下:
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
//发送本地广播
manager.sendBroadcast("your intent");
//注册本地广播
manager.registerReceiver("your broadcstreceiver");
//注销本地广播
manager.unregisterReceiver("your broadcstreceiver");
广播的作用
Android底层使用了Linux内核,Linux操作系统下面提供了很多种进程间通信方式,比如消息,管道,共享内存、Socket等等。但是Android系统并没有采用Linux的几种进程间通信方案,而是使用了binder来完成进程之间的通信。这主要是基于以下两点考虑:
- 效率,其底层使用了共享内存机制,提高了数据读写的效率
- 安全,从底层着手控制每个进程之间的访问权限,相比Linux在上层来控制访问权限要更加安全
安卓中的binder主要有两种方式来完成进程间通信:
- 采用代理模式,每个Client在访问Service之前都会获取一个Service的代理,然后通过这个代理来调用Service端提供的功能。
- 采用广播发送/接收的形式,也就是Broadcast和BroadcastReceiver。
最后,借用老罗对广播作用的描述:
在Android系统中,为什么需要广播机制呢?广播机制,本质上它就是一种组件间的通信方式,
如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一
个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。
然而,广播机制却是不可替代的,它和Binder机制不一样的地方在于,广播的发送者和接收者
事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在
一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
在软件工程中,是非常强调模块之间的高内聚低耦合性的,不然的话,随着系统越来越庞大,
就会面临着越来越难维护的风险,最后导致整个项目的失败。Android应用程序的组织方式,可
以说是把这种高内聚低耦合性的思想贯彻得非常透彻,在任何一个Activity中,都可以使用一个
简单的Intent,通过startActivity或者startService,就可以把另外一个Activity或者
Service启动起来为它服务,而且它根本上不依赖这个Activity或者Service的实现,只需要知道
它的字符串形式的名字即可,而广播机制更绝,它连接收者的名字都不需要知道。