Android 广播接收器专题
前言
Broadcast是Android四大组件中的组件之一,其发布-订阅的模式有利于四大组件间的通信,当我们需要在组件间通信或者进程间通信的时候,应考虑Broadcast。
BroadcastReceiver在Android中的应用场景
- 接收系统发送的广播信息
- 运用于组件间通信(应用内消息传递),比如Activity 和 Service 间的通信,可使用Broadcast(自定义广播)
- 跨应用消息传递
BroadcastReceiver的注册分类
- 通过清单文件注册广播(静态注册)
- 通过上下文注册广播(动态注册)
通过清单文件注册广播(静态注册)
静态注册在应用程序关闭的时候也能接收广播,系统软件管理器会在安装应用程序的时候注册接收器,然后接收器作为应用程序的单独入口点,当系统发送广播的时候,会遍历所有接受的接收器,遇到没有启动应用程序的接收器,系统会先启动应用程序,再发送广播让接收器接收。
代码事例如下:
<receiver android:name=".UstorageReceiver" android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED" />
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<data android:scheme="file"></data>
</intent-filter>
</receiver>
静态注册在注册清单中进行注册,其中android:exported为true表示可以跨进程接收,为false则是只接收
通过上下文注册广播(动态注册)
动态注册要看上下文对象来决定订阅对象,如果是Application为上下文对象,那么在应用程序启动没有被关闭进程之前都能接收到广播;假如Activity为上下文对象那么广播只能在Activity处于注册区间内接收到,比如一般我们需要确保注册和反注册的时候生命周期要对应 onCreate()对应 onDestory(),onResume()对应onStop()。
注册和反注册代码例子如下:
注册
BroadcastReceiver br = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);
反注册
this.unregisterReceiver(br)
Broadcast如何发送
发送Broadcast的分类
- 顺序广播
- 正常广播
- 粘性广播(API-21以上已移除,不建议使用)
顺序广播:每个顺序广播都有序号,按照序号逐个发送广播,优先的广播会把结果传递到下一个广播,以便在某些情况下可以对广播进行中止操作,通过设置android:priority 来定义顺序
正常广播:正常广播,也称无序广播,不可以中止广播,也不会把结果传递到下一个广播
如何发送Broadcast
顺序广播:
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
String receiverPermission=“android.permission.SEND_SMS";
sendOrderedBroadcast(intent);
Intent为意图对象;String
正常广播:
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);
从安全性考虑如何防止别人侵入自己的广播代码
- 为广播接收器添加权限
- 使用LocalBroadcastManager代替BroadcastReceiver
为广播接收器添加权限
在进行跨应用消息传递到场景中, BroadcastReceiver发送和注册的时候,Android提供了API对发送者和实施者进行了一定的限制。
发送应用限制权限
sendBroadcast(Intent, String)
sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
例子如下:
sendBroadcast(new Intent(“com.example.NOTIFY”), Manifest.permission.SEND_SMS);
当我们发送应用使用以上的API进行发送广播的时候,接收应用在注册清单中必须具备对应的Manifest.permission.SEND_SMS权限才能接收到发送应用发送过来到广播
<uses-permission android:name="android.permission.SEND_SMS"/>
接收应用限制权限
registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
接收方应用:
注册分为清单文件注册和上下文注册两种方式;
清单注册例子如下:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.SEND_SMS">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>
上下文注册如下:
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
为了能够发送信息到这些接收器,发送方的注册清单必须添加如下权限:
<uses-permission android:name="android.permission.SEND_SMS"/>
使用LocalBroadcastManager代替BroadcastReceiver
当我们仅仅运用Broadcast在应用内传递消息时,为了安全考虑使用权限限制会增加我们的代码量而显得没有必要,基于此Google 在support 包中引入了LocalBroadcastManager API 来发送广播,以支持应用内传递消息,而不需要做安全壁垒。
LocalBroadcastManager有如下优点:
- 只能在应用内传递消息,不用担心因其他应用脚本恶意发送广播导致的安全漏洞
- 相对于全局的重量级广播,LocalBroadcastManager的效率更高
LocalBroadcastManager创建对象:
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
LocalBroadcastManager注册广播接收器:
LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );
LocalBroadcastManager反注册广播接收器:
LocalBroadcastManager.unregisterReceiver( broadcastReceiver );
LocalBroadcastManager发送广播:
LocalBroadcastManager.sendBroadcast( intent ) ;
Broadcast版本兼容
在Android7.0以上的系统中。Google公司对广播7进行来一定的修改和优化,需要注意一些东西。
Android 9.0
- NETWORK_STATE_CHANGED_ACTION广播不再提供个人用户位置和有关个人身份数据的信息
- WI-FI的广播不再提供SSID、BSSID、连接信息和扫码结果,如果想要做类似的操作,请参考 getConnectionInfo()
Android 8.0
- 系统对清单文件注册做了一些限制,大多数隐式广播只能通过上下文注册,而不能使用清单注册
Android 7.0
- 删除ACTION_NEW_PICTURE、ACTION_NEW_VIDEO两种系统广播,当媒体产生变化时,Android7.0以上将不再发送这两个广播中的其中一个
- CONNECTIVITY_ACTION广播仅能使用上下文注册,在清单文件中注册将不起任何作用
为什么我们不要滥用Broadcast
- BroadcastReceiver作为在后台工作的组件,会消耗资源,影响手机用户体验。
- 有更轻量级、更高效的应用内消息传递机制代替Broadcast,比如EventBus、RxBus、LocalBroadcastManager 等。
常见的一些系统广播例子
监控U盘拨入拨出,获取U盘路径
注意添加:android:scheme
- android.intent.action.MEDIA_MOUNTED:sd卡被插入,且已经挂载
- android.intent.action.MEDIA_UNMOUNTED:sd卡存在,但还没有挂载
- android.intent.action.MEDIA_REMOVED:sd卡被移除
<receiver android:name=".UstorageReceiver" android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED" />
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<data android:scheme="file"></data>
</intent-filter>
</receiver>
获取U盘的路径为:
intent.getData().getPath()
耳机插入拔出状态
Intent.ACTION_HEADSET_PLUG:android.intent.action.HEADSET_PLUG
获取状态
intent.getIntExtra("state", 0)
当state为零时,耳机未插入;当state为1时,耳机为已插入;
屏幕相关的信息改变时
Intent.ACTION_SCREEN_ON:开屏
Intent.ACTION_SCREEN_OFF:锁屏