Android随笔4:观察者模式
1. 概念
1.1 什么是观察者模式
定义一个被观察者和一组观察者,观察者观察被观察者(订阅或者说是监听关系)。每当被观察者发生改变,会通知所有观察者,观察者收到通知可以做出对应的处理。
1.2 现实中的例子
其实现实生活当中有很多这样的例子,比如说直播订阅。看过斗鱼、虎牙直播的朋友肯定知道,很多主播在直播的时候都会吆喝这么一句话:“没有订阅的朋友们可以走一波订阅~”。
在这里,主播就相当于一个被观察者
,直播间的观众就是一个个观察者
,而订阅这个行为就是观察
。我们订阅主播之后,每当主播上线,都会发出一组通知
,通知所有的被观察者
,即所有订阅的观众------“我上线啦~快来我的直播间吧”。而观察者
(观众)在接受到通知之后,可以做出对应的处理:打开直播间观看直播,或者无视。淘宝的抢购预定、12306的候补购票也是同样的道理。所以,观察者模式也常被称为订阅者模式。
1.3 UML类图
抽象被观察者(Subject)
:它把所有对观察者对象的引用保存在一个集合中(比如ArrayList),每个被观察者都可以有任何数量的观察者。抽象被观察者提供接口:增加观察者、移除观察者、通知观察者状态发生改变。具体观察者(ConcreteSubject)
:实现抽象被观察者接口,将有关state存入具体观察者对象;在具体观察者的内部state改变时,给所有注册过的观察者发出通知。抽象观察者(Observer)
:为所有的具体观察者定义一个接口,在得到被观察者的通知时更新自己。具体观察者(ConcreteObserver)
:实现抽象观察者接口,在update接口中具体实现自己的更新逻辑。
2. 项目中的应用:网络切换
我本身是做网络一块的开发,之前在项目中有接到一个需求:在切换网络时,重新请求接口获取数据。下面,我会不使用观察者模式
和使用观察者模式
分别实现这个需求,然后来分析使用观察者模式的好处。
2.1 不使用观察者模式
public class NetMonitor {
private NetBroadcastReceiver netBroadcastReceiver;
private Context context;
public NetMonitor() {
}
/**
* 开启网络监听
*/
public void start(Context context) {
Context applicationContext = context.getApplicationContext();
this.context = applicationContext;
netBroadcastReceiver = new NetBroadcastReceiver();
IntentFilter filter = new IntentFilter();
//设置action为CONNECTIVITY_ACTION
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
//动态注册广播
applicationContext.registerReceiver(netBroadcastReceiver, filter);
}
/**
* 停止网络监听
*/
public void stop() {
if (context == null) {
return;
}
if (netBroadcastReceiver != null) {
context.unregisterReceiver(netBroadcastReceiver);
}
context = null;
}
private static class NetBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//如果action是网络变化,则执行以下处理逻辑
if (action != null && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
//执行一系列更新:
//更新网络状态
//发起网络探测
//请求接口,重新获取数据
//......
}
}
}
}
先来看一下NetBroadcastReceiver
这个内部静态类,这个继承了BroadcastReceiver
并实现了onReceive
方法。当有广播通知时,就会回调onReceive
方法。方法中判断了,如果action
是CONNECTIVITY_ACTION
,即网络状态发生了变化,才会继续执行后续的更新逻辑。
然后回头看一下start
方法,我们传入了context
上下文用来registerReceiver
,动态注册一个广播。重点关注一下filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
,我们知道IntentFilter的作用是过滤Intent,这行代码的意思就是我们的广播只接受CONNECTIVITY_ACTION
类型的action,避免接受不必要的广播通知。
stop
方法比较简单,就是在不需要监听网络变化的时候反注册。
讲完了实现,接下来具体看一下,接收到网络切换之后的处理逻辑。也就是onReceive
方法里面做的事情,我列举了很多可能需要在网络切换后做的逻辑:更新网络状态、发起网络探测、请求接口等…咋一看其实还好,就是一些方法的调用,代码也不复杂。但如果以后还有新的处理逻辑要加进来呢?那是不是又要在onReceive
方法中增加新的代码了?是的 ,这就是问题的所在,NetMonitor
这个类和具体的业务处理逻辑强耦合了,每当业务逻辑改变时,都要更新NetMonitor
的实现。而使用观察者模式,则能很好地将NetMonitor
类的职责和业务逻辑处理解耦。下面让我们来看一下具体的实现~
BTW,2.1说是不使用观察者模式,但是不知不觉当中还是实现了观察者模式,你发现了吗?
没错,就是BroadcastReceiver
!我们向系统注册广播的过程其实就相当于订阅,当网络状态发生改变时,我们得到通知,进行更新处理。
2.2 使用观察者模式
首先,我们需要定义一个网络状态变化的接口:
public interface OnNetworkChangeListener {
void onNetworkChange();
}
接口很简单,只有一个onNetworkChange
方法。
下面看一下NetMonitor
的实现:
public class NetMonitor {
private static List<OnNetworkChangeListener> onNetworkChangeListeners;
private NetBroadcastReceiver netBroadcastReceiver;
private Context context;
public NetMonitor() {
onNetworkChangeListeners = new ArrayList<>();
}
/**
* 开启网络监听
*/
//...
/**
* 停止网络监听
*/
//...
/**
* 注册监听
*/
public void addListener(OnNetworkChangeListener listener) {
if (listener != null) {
onNetworkChangeListeners.add(listener);
}
}
/**
* 注销监听
*/
public void removeListener(OnNetworkChangeListener listener) {
if (listener != null) {
onNetworkChangeListeners.remove(listener);
}
}
/**
* 通知网络状态更新
*/
private static void notifyNetworkChange() {
for (OnNetworkChangeListener listener : onNetworkChangeListeners) {
if (listener != null) {
listener.onNetworkChange();
}
}
}
private static class NetBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//如果action是网络变化,则执行以下处理逻辑
if (action != null && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
new Thread(new Runnable() {
@Override
public void run() {
notifyNetworkChange();
}
}).start();
}
}
}
}
start
和stop
方法一样和2.1一样,就不多加赘述了。观察上面这段代码,我们发现,NetMonitor
中增加了一个onNetworkChangeListeners
的数组,这个数组就是用来保存管理观察者注册的监听。观察者可以通过addListener
方法向NetMonitor
注册网络监听,也可以通过removeListener
来注销监听。而所有观察者注册的listener
就保存在onNetworkChangeListeners
的数组中。
再看一下onReceive
中的处理逻辑,很简约有没有~只是新开了一个线程,然后调用了notifyNetworkChange()
方法。那么notifyNetworkChange()
方法里面做了什么呢?没错,遍历onNetworkChangeListeners
数组,然后调用listener.onNetworkChange()
,通知所有观察者进行更新。而观察者只需要实现OnNetworkChangeListener
接口,在notifyNetworkChange
回调中实现各自的更新逻辑即可。比如:
public class MainActivity extends AppCompatActivity implements OnNetworkChangeListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NetMonitor netMonitor = new NetMonitor();
netMonitor.start(this.getApplicationContext());
netMonitor.addListener(this);
}
@Override
public void onNetworkChange() {
//更新操作
}
}
NetMonitor
并不关心网络切换之后具体要做什么样的处理,它只关心网络切换了,然后把这个信息通知给所有注册过监听的观察者,而具体的处理是观察者应该关心的事。这样就达成了NetMonitor
的解耦,它只负责网络状态变化的通知,具体的处理交给许许多多的观察者实现。
3. 总结
如果项目中存在一对多的依赖关系时,就可以考虑使用观察者模式。其实Button
中的点击事件也使用到了观察者模式,Button
在这里其实就是被观察者,然后我们通过setOnClickListener
向Button
注册监听,实现了onClick
方法,去实现点击之后的具体处理逻辑。还有很多很多的例子,像大名鼎鼎的RxJava
框架,其核心也是观察者模式。
4. demo代码
demo地址: git跳转.