美文网首页Android开发Android知识
从RecyclerView开始,研究观察者模式

从RecyclerView开始,研究观察者模式

作者: OnPush | 来源:发表于2017-05-08 15:16 被阅读0次

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的源码,这两个开源库的实现也是运用了观察者模式。

相关文章

网友评论

    本文标题:从RecyclerView开始,研究观察者模式

    本文链接:https://www.haomeiwen.com/subject/mlrstxtx.html