阅读 266

详解观察者模式及其应用

本文已参与好文召集令活动,点击查看:[后端、大前端双赛道投稿,2万元奖池等你挑战!]

认识观察者模式

假设现在有一家报社,它的业务就是出版报纸,现在我作为一个用户订阅了这家报社的报纸,只要有新的报纸出版,就会给我送过来。突然有一天,我不想看报纸了,就取消了对这家报社的订阅,这样他们就不会给我送报纸了,当然只要这家报社一直在运营,就会一直有像我一样的人订阅和取消订阅。

上边的这个例子就是一个典型的观察者模式:出版社+订阅者=观察者模式。只不过这个名称需要再改动一下,出版社改为“主题”也就是“Subject”,订阅者改为“观察者”,也就是“Observer”。

image.png

通过一张图来更清晰的展示一下,可以看到,观察者模式定义了一系列对象之间的一对多的关系,当一个对象发生改变,其他依赖者都会收到通知。多个观察者依赖于某一个主题,只要主题发生改变,观察者就会根据收到的通知进行相应的操作。

简单实现

说了那么多废话,还是要实践出真知啊~

类图

首先看看它的类图,有了指导方针写代码才有方向啊。

image.png

角色介绍
  • Subject:抽象主题,也就是被观察(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者
  • ConcreteSubject:具体主题,该角色将有关状态存入具体的观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,也可以叫做具体被观察者(ConcreteObservable)
  • Observer:抽象观察者,定义了一个更新接口,在得到主题更改的通知时可以更细自己
  • ConcreteObserver:具体观察者,实现抽象观察者所定义的接口,在主题发生状态改变时更新自己。

简单实现

  1. 抽象观察者
//1.抽象观察者
interface Observe{
    void dispatch();
}
复制代码
  1. 抽象被观察者
//2.抽象主题 被观察者
interface Subject{
    void removeObserve(Observe observe);
    void addObserve(Observe observe);
    void post();	
}
复制代码
  1. 具体观察者
//3.具体观察者
class MyObserve implements Observe{
	String name;
	public MyObserve(String name) {
		this.name = name;
	}
	@Override
	public void dispatch() {
		System.out.println(name+"收到通知");
	}
	
}
复制代码
  1. 具体被观察者
//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是抽象的主题角色。

  1. 定义具体观察者
//具体观察者
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);
	}
	
}
复制代码
  1. 定义具体主题
//具体主题
class MySubject2 extends Observable{
	
	public void post(String content){
		//标识状态或者内容发生改变
		setChanged();
		//通知所有的观察者,参数为要发送的数据
		notifyObservers(content);
	}
}
复制代码
  1. 测试一下
	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("又一个新消息");
	}
复制代码
  1. 查看结果
2号观察者你好,主题更新了,内容是新消息
1号观察者你好,主题更新了,内容是新消息
1号观察者你好,主题更新了,内容是又一个新消息
复制代码

好处

观察者模式提供了一种对象设计,让主题和观察者之间松耦合。关于观察者的一切,主题只知道观察者实现了接口(Observer),主题不需要知道观察者的具体实现类是谁,做了什么以及其内部的细节。而且任何时候,我们都可以增加新的观察者,主题唯一依赖的是一个实现了Observer接口的对象列表,对于这个列表可以随意的增加和删除。当有新类型的观察者出现时,主题的代码也不需要修改,所要做的就是在新的类里实现此观察者接口,然后注册为观察者就可以了。主题不在乎别的,只会发送通知给所有实现了观察者接口的对象。

改变主题或者观察者其中的一方,都不会影响另一方,两者是松耦合的,只要他们之间的接口仍然守约,我们就可以自由的改变他们。

Android中的使用场景

RecyclerView使用观察者模式

当我们调用notifyDataSetChanged()方法时,就可以刷新列表了,这么神奇的设计是怎么样实现的呢?我们去源码中一探究竟

  1. 要实现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方法中调用了mObservablenotifyChanged方法,那么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();
}
复制代码

观察者的注册和解绑都是在这个方法中进行的,通过unregisterAdapterDataObserverregisterAdapterDataObserver这两个方法来实现。

//解绑
public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
    mObservable.unregisterObserver(observer);
}
复制代码
//注册
public void registerAdapterDataObserver(AdapterDataObserver observer) {
    mObservable.registerObserver(observer);
}
复制代码

这两个方法最终还要需要通过mObservable被观察者的unregisterObserverregisterObserver来实现。这两个方法相信你已经很熟悉了吧。 好了关于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观察者的实现,有两种方式实现:

  1. 实现LifecycleObserver接口,在相关声明周期的方法上加上OnLifecycleEvent注解,这种方案,会根据注解利用APT在编译期自动生成GeneratedAdapter实现类,只有一个方法callMethods,当声明周期改变时,进行回调该方法。
  2. 第二种方法是实现DefaultLifecycleObserver,适用于java8及以上,使用java8的default关键字空实现了FullLifecycleObserver的所有方法

无论哪种方案,都会实现onStateChanged方法,因为最终都会调用观察者的onStateChanged方法。

至此,LifeCycle跟观察者模式相关的几个核心类就说完了,当然内部的实现还要更复杂,还需要进一步的去研究。

结语

本文介绍了观察者模式的几个角色,并简单的对其进行了实现,还从Android的角度对其进行了分析,当然了观察者模式在Android中的应用还有很多,比如EventBus、Rxjava、LiveData等等,越来越多的组件都开始集成观察者模式,也说明了观察者模式的重要性。 希望阅读本文可以对你有所启发。

参考资料

文章分类
Android
文章标签