Android开发中一个很常见的场景是有一个列表,列表上每条Item有个点赞的按钮,当你给这条Item点赞的时候,这个按钮就会变成已点赞状态,相信大家可以脑补到这个场景。这里就用到了观察者模式。
那么什么是观察者模式,我们稍微瞧一下这个模式的定义,不用太在意,看不懂也没关系,我觉得设计模式是需要用代码感受的,加这些定义只是为了让懂的人感受更深,更系统。
观察者模式:
两个抽象类或者接口,
一个是被观察者的抽象类或者接口,需要持有所有观察者的引用,
被观察者要能做三件事儿:
1.可以注册观察者。
2.有注册就有注销。
3.可以通知观察者的方法。
一个是观察者的抽象类或者接口,用来更新自己。
观察者要能做一件事儿,那就是更新自己。
具体举个例子:
虽然我们主要是写android的,但是服务器的东西还是要懂点,就拿简书这个类似于博客的网站来说吧,如果你订阅了或者说关注了一个领域,就能收到这个领域文章的推送,如果没有关注,则不能。是相当于有一个总控制台(被观察者,持有数据源,这里的数据源是我们每个订阅了的人)通知下面的观察者。
在java中可以这么写。(因为java的观察者模式是内置的,所以你只需要实现Observer接口和继承Observable)
观察者的代码:
/**
* 程序员,也就是你,订阅这个专题的人是。观察者
*/
public class Coder implements Observer {
private String yourName;
public Coder(String yourName){
this.yourName=yourName;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("你订阅的"+arg.toString()+"更新了。");
}
@Override
public String toString() {
return "your name "+yourName;
}
}
以下是被观察者和服务器的代码。
/**
* 你订阅的简书android领域
*/
public class JianShuAndroid extends Observable{
public void postNewContentToCoder(String content){
setChanged();
notifyObservers(content);
}
}
/**
* 服务器的代码
*/
public class Server{
public static void main(String[] args){
JianShuAndroid jianShuAndroid=new JianShuAndroid();
Coder coder1=new Coder("name1");
Coder coder2=new Coder("name2");
Coder coder3=new Coder("name3");
jianShuAndroid.addObserver(coder1);
jianShuAndroid.addObserver(coder2);
jianShuAndroid.addObserver(coder3);
jianShuAndroid.postNewContentToCoder("contentChanged");
}
}
(这里顺便提一下,为什么观察者是实现一个observer接口,而被观察者是继承一个抽象类呢?我觉得,被观察者写成抽象类的原因是复用,观察者写成接口的原因是降低代码的耦合度,面向接口编程,在原则里就是依赖倒置原则,我们倒着思考,如果这里不是接口,而是一个具体的类,那么,耦合度就相当高了,如果不是Coder注册就无法添加到observable里,就要修改observable的代码。)
接下来验证我们看看android的套路是怎么玩的。
文章开头提到的RecyclerView,整个列表是一个被观察者也就是这个RecyclerView的Adapter,为什么要叫被观察者呢?因为Adapter持有了所有数据源,而每条Item要做的是观察这些数据源有没有变化,所以就叫被观察者,每个Item就叫观察者。当数据变化的时候,adapter就通知这条Item,数据源发生了变化了(这个通知的过程是notifyDataSetChanged来进行的),我们可以猜想,RecyclerView的内部的模式是观察者模式。
玩观察者模式,假如你用的是java,那么observable,observer是内置的,你的被观察者继承observable,观察者实现observer接口,就行了。其他的语言,如果你搞清了原理,自己写一个也很简单。
上面我们说到了RecyclerView,开始分析(看源码,一万多行的RecyclerView不要急,找我们想要的。我感觉看源码就是像给了你一团毛线让你理顺,当然你要去找那个线头,千万不要陷入泥潭,每一行都看。)
大家找一下这个突破口,也就是线头,我们明显现在已经知道了recyclerview里肯定用到了观察者模式,还有一个重要线索是,我们知道它使怎么通知观察者改变的,就是notifyDataSetChanged。那就从这个开始看起。
找了半天,知道了notifyDataSetChanged是在Adapter里面,而这个adapter开始的第一句就是
public static abstract class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
private boolean mHasStableIds = false;
....
....
....
}
很熟悉的字眼,observable(被观察者)来了。看一下这个AdapterDataObservable,不要怕,名字是长了点,但是一般名字长的都是纸老虎。
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
return !mObservers.isEmpty();
}
public void notifyChanged() {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
}
...
...
...
一看,没错这家伙肯定是被观察者,因为它继承了Observable。我们那个简书网站就是这样啊,JianShuAndroid继承了observable。
还记得我们前面说被观察者一般要可以做三件事儿。注册,注销和通知。那么这里也不会例外。我们看到的是通知这件事儿,但是注册和注销是java内部的Observable帮我们实现了。
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* Adds an observer to the list. The observer cannot be null and it must not already
* be registered.
* @param observer the observer to register
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is already registered
*/
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);
}
}
/**
* Removes a previously registered observer. The observer must not be null and it
* must already have been registered.
* @param observer the observer to unregister
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is not yet registered
*/
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);
}
}
....
....
....
这时候observable出现了,那observer呢?
其实上面就已经出现了。回上一段代码里找,AdapterDataObserver的泛型里是AdapterDataObserver,然后我们全局搜一下就可以发现
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
if (mAdapter.hasStableIds()) {
// TODO Determine what actually changed.
// This is more important to implement now since this callback will disable all
// animations because we cannot rely on positions.
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
} else {
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
}
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
...
...
...
这就是observer了,这个是干嘛的,根据我们上面说的,有一件事儿,那就是更新数据的,onChanged里面涉及到requestLayout()的,在requestLayout里会重新调用onDraw来重绘。
最后整理一遍这个过程,
在Adapter里面有一个AdapterDataObservable,是被观察者,被观察者必须有三个方法,注册,销毁,通知,这里的注册就是registerAdapterDataObserver,通知就是notify相关的。
在setAdapter的时候,将观察者,也就是RecyclerViewDataObserver注册到AdapterDataObservable里面来维护,观察者里面自然是更新布局。
我们调用notifyDataSetChanged其实就是调用被观察者的notify相关方法
关于观察者模式,有兴趣的话可以看看EventBus和AndroidEventBus的源码,这两个开源库的实现也是运用了观察者模式。
网友评论