详解观察者模式及其应用
认识观察者模式
假设现在有一家报社,它的业务就是出版报纸,现在我作为一个用户订阅了这家报社的报纸,只要有新的报纸出版,就会给我送过来。突然有一天,我不想看报纸了,就取消了对这家报社的订阅,这样他们就不会给我送报纸了,当然只要这家报社一直在运营,就会一直有像我一样的人订阅和取消订阅。
上边的这个例子就是一个典型的观察者模式:出版社+订阅者=观察者模式。只不过这个名称需要再改动一下,出版社改为“主题”也就是“Subject”,订阅者改为“观察者”,也就是“Observer”。
通过一张图来更清晰的展示一下,可以看到,观察者模式定义了一系列对象之间的一对多的关系,当一个对象发生改变,其他依赖者都会收到通知。多个观察者依赖于某一个主题,只要主题发生改变,观察者就会根据收到的通知进行相应的操作。
简单实现
说了那么多废话,还是要实践出真知啊~
类图
首先看看它的类图,有了指导方针写代码才有方向啊。
角色介绍
- Subject:抽象主题,也就是被观察(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者
- ConcreteSubject:具体主题,该角色将有关状态存入具体的观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,也可以叫做具体被观察者(ConcreteObservable)
- Observer:抽象观察者,定义了一个更新接口,在得到主题更改的通知时可以更细自己
- ConcreteObserver:具体观察者,实现抽象观察者所定义的接口,在主题发生状态改变时更新自己。
简单实现
- 抽象观察者
//1.抽象观察者
interface Observe{
void dispatch();
}
- 抽象被观察者
//2.抽象主题 被观察者
interface Subject{
void removeObserve(Observe observe);
void addObserve(Observe observe);
void post();
}
- 具体观察者
//3.具体观察者
class MyObserve implements Observe{
String name;
public MyObserve(String name) {
this.name = name;
}
@Override
public void dispatch() {
System.out.println(name+"收到通知");
}
}
- 具体被观察者
//4.具体主题
class MySubject implements Subject{
//观察者集合
ArrayList<Observe> list = new ArrayList<>();
//通知所有的观察者
@Override
public void post() {
for(int i = 0 ;i<list.size();i++){
list.get(i).dispatch();
}
}
//添加观察者
@Override
public void addObserve(Observe observe) {
list.add(observe);
}
//删除观察者
@Override
public void removeObserve(Observe observe) {
list.remove(observe);
}
}
测试一下
public static void main(String[] args) {
Observe observe1 = new MyObserve("1号观察者");
Observe observe2 = new MyObserve("2号观察者");
Subject subject = new MySubject();
subject.addObserve(observe1);
subject.addObserve(observe2);
subject.post();
subject.removeObserve(observe2);
subject.post();
}
看一下打印结果
1号观察者收到通知
2号观察者收到通知
1号观察者收到通知
使用java内置的观察者模式
Observer和Observable是JDK内置的类型,Observer是抽象的观察者角色,Observable是抽象的主题角色。
- 定义具体观察者
//具体观察者
class MyObserve2 implements Observer{
String name;
public MyObserve2(String name) {
this.name = name;
}
//接收通知的方法,参数arg为主题发送过来的数据,参数Observable表示是哪个主题发送的通知
@Override
public void update(Observable Observable, Object arg) {
System.out.println(name+"你好,主题更新了,内容是"+arg);
}
}
- 定义具体主题
//具体主题
class MySubject2 extends Observable{
public void post(String content){
//标识状态或者内容发生改变
setChanged();
//通知所有的观察者,参数为要发送的数据
notifyObservers(content);
}
}
- 测试一下
public static void main(String[] args) {
MySubject2 subject2 = new MySubject2();
MyObserve2 observe1 = new MyObserve2("1号观察者");
MyObserve2 observe2 = new MyObserve2("2号观察者");
subject2.addObserver(observe1);
subject2.addObserver(observe2);
subject2.post("新消息");
subject2.deleteObserver(observe2);
subject2.post("又一个新消息");
}
- 查看结果
2号观察者你好,主题更新了,内容是新消息
1号观察者你好,主题更新了,内容是新消息
1号观察者你好,主题更新了,内容是又一个新消息
好处
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。关于观察者的一切,主题只知道观察者实现了接口(Observer),主题不需要知道观察者的具体实现类是谁,做了什么以及其内部的细节。而且任何时候,我们都可以增加新的观察者,主题唯一依赖的是一个实现了Observer接口的对象列表,对于这个列表可以随意的增加和删除。当有新类型的观察者出现时,主题的代码也不需要修改,所要做的就是在新的类里实现此观察者接口,然后注册为观察者就可以了。主题不在乎别的,只会发送通知给所有实现了观察者接口的对象。
改变主题或者观察者其中的一方,都不会影响另一方,两者是松耦合的,只要他们之间的接口仍然守约,我们就可以自由的改变他们。
Android中的使用场景
RecyclerView使用观察者模式
当我们调用notifyDataSetChanged()
方法时,就可以刷新列表了,这么神奇的设计是怎么样实现的呢?我们去源码中一探究竟
- 要实现RecyclerView的功能,需要给其设置一个adapter,这个adapter需要继承RecyclerView的内部类,看一下它的内部实现
public abstract static class Adapter<VH extends ViewHolder>{
private final AdapterDataObservable mObservable = new AdapterDataObservable();
...
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
...
}
notifyDataSetChanged这个方法调用了mObservable的notifyChanged方法,来看看这个AdapterDataObservable
是什么
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
return !mObservers.isEmpty();
}
public void notifyChanged() {
//遍历观察者,调用onChanged方法
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
...
有没有很熟悉的感觉,它继承了Observable
这个抽象类,也就是我们之前说的抽象主题类,当然这个Observable
并不是JDK中那个,这个是Android自己实现的,不过中心思想还是一样的,同样是一个观察者的集合,通过注册registerObserver
和取消unregisterObserver
的方法来管理这个观察者集合
public abstract class Observable<T> {
protected final ArrayList<T> mObservers = new ArrayList<T>();
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
看到这里我们可以知道AdapterDataObservable
就是一个被观察者了,在adapter中持有一个AdapterDataObservable
的引用mObservable
,在notifyDataSetChanged
方法中调用了mObservable
的notifyChanged
方法,那么notifyChanged
方法做了什么呢?没错就是遍历观察者mObservers
然后调用其onChanged
方法。接下来我们看一下这个 onChanged方法的具体实现在哪里吧
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
...
}
在这个类的实现中调用了requestLayout
方法,去重新申请布局的绘制流程。
到这里,RecyclerView中的观察者和被观察者都找到了,那么是在一个什么样的情况下,进行注册和解绑的呢?当我们使用RecyclerView的时候,需要给其设置一个adapter,就从这里入手吧
public void setAdapter(Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
requestLayout();
}
我们看到在setAdadapter中自动调用了requestLayout()去申请布局的绘制,还有一个比较重要的方法setAdapterInternal(),我们点进去看看
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
//如果adapter不为空,则先解除旧的观察者对象
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
...
mAdapter = adapter;
if (adapter != null) {
//为新的adapter注册观察者
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
}
观察者的注册和解绑都是在这个方法中进行的,通过unregisterAdapterDataObserver
和registerAdapterDataObserver
这两个方法来实现。
//解绑
public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
mObservable.unregisterObserver(observer);
}
//注册
public void registerAdapterDataObserver(AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
这两个方法最终还要需要通过mObservable
被观察者的unregisterObserver
和registerObserver
来实现。这两个方法相信你已经很熟悉了吧。
好了关于RecyclerView的观察者模式就写完了。除了在RecyclerView中使用了观察者模式,其他的地方有用到吗?当然有,观察者模式的使用范围在Android中真的是太广泛了,我们再来看一个。
LifeCycle中使用观察者模式
通过以上几个例子的讲解,会发现凡是应用了观察者模式的,寻找观察者和被观察者显得尤为重要。
首先把其相关的几个核心类找出来:
- 被观察者:lifecycleOwner
- 观察者:LifecycleObserver
- 存储观察者的容器:LifecycleRegistry
LifeCycle中被观察者就是LifecycleOwner
,它只有一个方法
public interface LifecycleOwner {
Lifecycle getLifecycle();
}
Android Support包26以上的Fragment和AppCompatActivity已经默认实现了这个接口,所以他们也是LifeCycle中的被观察者,看一下Fragment中的实现
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner {
LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
void performCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState);
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
void performStart() {
onStart();
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
void performResume() {
onResume();
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}
void performPause() {
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
onPause();
}
void performStop() {
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
onStop();
}
void performDestroy() {
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
onDestroy();
}
}
fragmnet中真正作为LifeCycle的是mLifecycleRegistry,而mLifecycleRegistry继承自Lifecycle
public abstract class Lifecycle {
public abstract void addObserver(@NonNull LifecycleObserver observer);
public abstract void removeObserver(@NonNull LifecycleObserver observer);
public abstract State getCurrentState();
public enum Event {
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_ANY
}
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
}
LifeCycle这个类中定义了 添加观察者、移出观察者、获取状态、Event、State这些方法和枚举,在其唯一的实现类LifecycleRegistry中完成了具体的实现。具体的实现可以去参考源码。
最后再来说说LifecycleObserver观察者的实现,有两种方式实现:
- 实现LifecycleObserver接口,在相关声明周期的方法上加上OnLifecycleEvent注解,这种方案,会根据注解利用APT在编译期自动生成GeneratedAdapter实现类,只有一个方法callMethods,当声明周期改变时,进行回调该方法。
- 第二种方法是实现DefaultLifecycleObserver,适用于java8及以上,使用java8的default关键字空实现了FullLifecycleObserver的所有方法
无论哪种方案,都会实现onStateChanged方法,因为最终都会调用观察者的onStateChanged方法。
至此,LifeCycle跟观察者模式相关的几个核心类就说完了,当然内部的实现还要更复杂,还需要进一步的去研究。
结语
本文介绍了观察者模式的几个角色,并简单的对其进行了实现,还从Android的角度对其进行了分析,当然了观察者模式在Android中的应用还有很多,比如EventBus、Rxjava、LiveData等等,越来越多的组件都开始集成观察者模式,也说明了观察者模式的重要性。 希望阅读本文可以对你有所启发。
参考资料
转载自:https://juejin.cn/post/6986819271951122446