美文网首页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