1 概要
BroadcastReceiver组件是一种消息型组件,主要用于在不同组件乃至不同应用之间传递消息。因为它工作在系统内部,无法被用户所感知。
BroadcastReceiver也叫做广播,广播的注册方式有两种:静态注册和动态注册。静态注册指在AndroidManifest中注册广播,这种广播在应用安装时被系统解析,此种形式的广播不需要应用启动就可以接收到相应的广播。动态广播需要通过Context.registerReceiver()来实现,并且在不需要的时候通过Context.unRegisterReceiver()解除广播,此种形态的广播必须要应用启动才能注册并接收广播。在实际开发中通过Context的一系列send方法来发送广播,被发送的广播会被系统(Activity Manager Service)发送给合适的广播接受者,依据是intent-filter/permission。由此发现,BroadcastReceiver可以用来实现低耦合的观察者模式,一般来说不需要停止,也没有停止的概念。由于BroadcastReceiver运行在主线程,所以它不适合用来执行耗时操作。
2 实现原理
2.1 采用模型
Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。因此,Android将广播的发送者和接收者解耦,使得系统方便集成,更易扩展。
2.2 模型讲解
模型中有3个角色:消息订阅者(广播接收者)、消息发布者(广播发布者)、消息中心(AMS,即Activity Manager Service),示意&原理图如下:
图片来源于:Android四大组件:BroadcastReceiver史上最全面解析
2.3 流程
图片来源于:Android四大组件:BroadcastReceiver史上最全面解析
3 使用流程
3.1 自定义全局广播接收者BroadcastReceiver
// 继承BroadcastReceivre基类
public class mBroadcastReceiver extends BroadcastReceiver {
// 复写onReceive()方法,接收到广播后,则自动调用该方法
@Override
public void onReceive(Context context, Intent intent) {
// 默认情况下,广播接收器运行在UI线程,因此onReceive()方法不能执行耗时操作,否则将导致ANR.
// 写入接收广播后的操作
}
}
3.2 广播接收器注册
3.2.1 静态注册
(1)属性说明
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的:如果有intent-filter,默认值为true,否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程,可以指定独立的进程
//注:Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >
//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
(2)注册示例
<receiver
//此广播接收者类是mBroadcastReceiver
android:name=".mBroadcastReceiver" >
//用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
3.2.2 动态注册
// 选择在Activity生命周期方法中的onResume()中注册
@Override
protected void onResume(){
super.onResume();
// 1. 实例化BroadcastReceiver子类&IntentFilter
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
// 2. 设置接收广播的类型
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
// 3. 动态注册:调用Context的registerReceiver()方法
registerReceiver(mBroadcastReceiver, intentFilter);
}
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
@Override
protected void onPause() {
super.onPause();
//销毁在onResume()方法中的广播
unregisterReceiver(mBroadcastReceiver);
}
3.2.3 两种注册方式的区别
图片来源于:Android四大组件:BroadcastReceiver史上最全面解析
4 广播发送者向AMS发送广播
4.1 广播的发送
广播是用”Intent标识,定义广播的本质 = 定义广播所具备的Intent,广播发送=广播发送者将此广播的Intent通过sendBroadcast()方法发送出去。
4.2 广播的类型
4.2.1 普通广播(Normal Broadcast或无序广播)
即开发者自身定义intent的广播(最常用)。发送广播使用如下:
Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);
<receiver
//此广播接收者类是mBroadcastReceiver
android:name=".mBroadcastReceiver" >
//用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="BROADCAST_ACTION" />
</intent-filter>
</receiver>
4.2.2 系统广播(System Broadcast)
Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播。每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:
具体广播类型:Android常用系统广播
注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播
4.2.3 有序广播(Ordered Broadcast)
(1)定义:发送出去的广播被广播接收者按照先后顺序接收,有序是针对广播接收者而言的。
(2)广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者)
- 按照Priority属性值从大-小排序;
- Priority属性相同者,动态注册的广播优先;
(3)特点
- 接收广播按顺序接收;
- 先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
- 先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播。
(4)具体使用:有序广播的使用过程与普通广播非常类似,差异仅在于广播的发送方式
sendOrderedBroadcast(intent);
4.2.4 App应用内广播(Local Broadcast)
(1)背景:Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)
(2)冲突
- 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
- 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息,即会出现安全性 & 效率性的问题。
(3)解决方案:使用App应用内广播。App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。相比于全局广播(普通广播),App应用内广播优势体现在:安全性高&效率高。
(4)具体使用(不能静态注册):使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例。
private String BROADCAST_ACTION = "com....action.balanceChanged";
// 1、注册广播接收器
//(1):实例化BroadcastReceiver子类&IntentFilter mBroadcastReceiver
BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
//(2):实例化LocalBroadcastManager的实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
//(3):设置接收广播的类型
intentFilter.addAction(BROADCAST_ACTION);
//(4):调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册
localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter);
// 2、发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent)
// 3、取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(broadcastReceiver);
4.2.5 粘性广播(Sticky Broadcast)
由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。
5 避免在onReceive()中执行耗时操作
默认情况下,广播接收器是在主线程执行,因此onReceive()方法不能执行耗时操作,方法耗时超过10秒钟,将导致ANR。
**不能使用子线程来解决 , 因为BroadcastReceiver的生命周期很短 , 子线程可能还没有结束,BroadcastReceiver就先结束了。**BroadcastReceiver一旦结束 , 此时BroadcastReceiver所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 )。如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的。
可以使用IntentService 、 创 建HandlerThread或者调用Context的registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他Wroker线程执行onReceive的方法。
Android性能优化之IntentService
6 在Android中用广播来更新UI界面好吗?
(1)BroadcastReceiver有生命周期,但很短。当它的onReceive()方法执行完成后,它的生命周期也就随之结束了。这时候由于BroadcastReceiver已经不处于active状态,所以极有可能被系统干掉。也就是说如果在onReceive()去开线程进行异步操作或者打开Dialog都有可能在没达到你要的结果时,进程就被系统杀掉了。
(2)实际开发中其实大多数情况都是可以采用BroadcastReceiver来更新UI。
(3)对于超过10秒更新UI,不推荐这种方式。因为:Receiver也是运行在主线程的,不能做耗时操作。虽然超时时间有10秒,但不意味着所有的更新UI界面操作时间都在安全范围之内。
(4)对于频繁更新UI,也不推荐这种方式。因为:Android 广播的发送和接收都包含了一定的代价,它的传输都是通过Binder进程间通信机制来实现的,那么系统会为了广播能顺利传递而做一些进程间通信的准备。而且可能会由于其它因素导致广播发送和到达延迟。
(5)学习链接
用广播 BroadcastReceiver 更新 UI 界面真的好吗?
7 注意
(1)动态广播最好在Activity的onResume()注册、onPause()注销?
因为onPause()在Activity销毁前一定会被执行(内存不足要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁),从而保证广播在Activity销毁前一定会被注销,从而防止内存泄露。但是在onPause()必须判断当前Activity是否已经销毁,销毁了才注销。
(2)一个app被杀掉进程后,是否还能收到广播?
静态注册的常驻型广播接收器还能接收广播,所以本地广播不能用静态注册。
(3)对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
- 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestricted Context;
- 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
- 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。
- 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context;
8 转载链接&参考链接
Android四大组件:BroadcastReceiver史上最全面解析
9 项目应用例子
(1)定义广播接收器
/**
* 刷新activity的广播接收器
*/
public class RefreshReadActReceiver extends BroadcastReceiver {
private static final String ACTION_FONT_CHANGED = "com...action.fontChanged"; // 字体改变
private static final String ACTION_LOGIN_STATUS_CHANGED = "com...action.loginStatusChanged"; // 登录状态改变
private final RefreshObserver mObserver;
public static IntentFilter createIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_FONT_CHANGED);
intentFilter.addAction(ACTION_LOGIN_STATUS_CHANGED);
return intentFilter;
}
public RefreshReadActReceiver(RefreshObserver observer) {
this.mObserver = observer;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
String action = intent.getAction();
if (ACTION_FONT_CHANGED.equals(action)) {
mObserver.fontChanged();
} else if (ACTION_LOGIN_STATUS_CHANGED.equals(action)) {
mObserver.loginStatusChanged();
}
}
public interface RefreshObserver {
void fontChanged();
void loginStatusChanged();
}
}
(2)注册广播&逻辑处理
public class GroupActivity extends AbstractMvpActivity {
private static final String TAG = "GroupActivity";
private LocalBroadcastManager mLocalBroadcastManager;
private RefreshReadActReceiver mRefreshReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver();
}
@Override
protected void onDestroy() {
super.onDestroy();
unRegisterReceiver();
}
/**
* 获取布局ID,在子类重写该方法
*/
@Override
protected int getLayoutId() {
return R.layout.activity_group;
}
/**
* 注册登录成功回调广播
*/
protected void registerReceiver() {
mRefreshReceiver = new RefreshReadActReceiver(new RefreshReadActReceiver.RefreshObserver() {
@Override
public void fontChanged() {
}
@Override
public void loginStatusChanged() {
// 刷新详情
// getGroupData();
}
});
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
mLocalBroadcastManager.registerReceiver(mRefreshReceiver, RefreshReadActReceiver.createIntentFilter());
}
/**
* 反注册广播
*/
protected void unRegisterReceiver() {
if (mLocalBroadcastManager == null || mRefreshReceiver == null) {
return;
}
try {
mLocalBroadcastManager.unregisterReceiver(mRefreshReceiver);
} catch (Exception e) {
e.printStackTrace();
}
mRefreshReceiver = null;
mLocalBroadcastManager = null;
}
}