Android设计模式--观察者模式

153 阅读5分钟

一、概述

1.1观察者模式介绍

观察者模式是一个使用频率非常高的模式,他最常用的地方是GUI系统和订阅-发布系统。 该模式的最重要的作用就是解耦,使观察者和被观察者之间依赖尽可能小,甚至好无依赖。

1.2观察者模式的定义

定义对象间一种一对多的关系,使得每当一个对象改版状态,所有依赖它的对象都会得到通知并更新。 如图:

1.3观察者模式的使用场景

1.事件多级触发场景
2.关联行为场景
3.跨系统的消息交换场景,如:消息队列,事件总线的处理机制

举几个简单的例子说明一下:

  • 例一:生活中,我们一群人围着锅吃饭,饭好了,我们就开吃。(观察者:人们,被观察者:饭)
  • 例二:Android中,最常见的点击事件,通过设置控件的OnClickListener并传入一个OnClickListener的实现类来回调点击事件。(观察者:OnClickListener,被观察者:控件)
  • 例三:Android中,我们从A页面–>B页面–>C页面–>D页面–>F页面…. 我们想把A页面信息传递给最后一个页面,如果通过页面传递那么很繁琐,我们直接可以在需要的页面去订阅A页面的事件,当A页面刷行数据,其他订阅了A页面事件的就可以直接接受数据。(相当于少了中间商赚差价,是不爽了很多,而且效率还比较高)
  • 例四:Android中,我们常用的recyclerView,listView刷行数据时调用notifyDataSetChanged()来更新ui,想知道具体原因,那么请仔细往下看完这篇文章。
  • 例五:Android中,我们通常发送一个广播,凡是注册了该广播的都可以接收到该广播,这也是Android中典型的观察者模式。

1.4观察者模式的UML

Subject:抽象主题,把所有观察者对象的引用保持再一个集合,每个主题可以有多个观察者(一对多)。
ConcreteSubject:具体主题,在内部发生变化是后,通知所有注册过的观察者。
Observer:抽象观察者,定义了一个更新接口,得到主题更改通知的时候更新自己。
ConcreteObserver:具体的观察者,实现了抽象观察者的更新接口,以便在主题状态发生变化时候更新自身状态。

二、简单实现

我们假设开发一款新闻的软件,每当有新的内容推出,那么只要是订阅了该新闻的用户都可以接收到该新闻。 这种模式叫做发布–订阅模式,也称为观察者模式。

demo下载地址:download.csdn.net/download/gi…

这里写图片描述

首先我们需要完成被观察者,这里新闻网站是被观察对象。

/**
 * Created by doudou on 2017/12/5.
 *
 * 新闻网站----被观察者
 */

public class News extends Observable{
    public void postNewPublication(String content){
        //标识状态或者内容发送改变
        setChanged();
        //通知所有观察者
        notifyObservers(content);
    }
}

然后去完成观察者:

/**
 * Created by doudou on 2017/12/5.
 *
 * 订阅新闻的用户-----观察者
 */

public class User implements Observer{
    public String name;
    public MainActivity activity;
    private String msg;

    public User(String name,  MainActivity activity) {
        this.name = name;
        this.activity = activity;
    }

    @Override
    public void update(Observable observable, Object o) {
        String news = "Hi,"+name+",发布新内容啦!!"+o;
        Log.e("binli",news);
    }

}

接着我们就可以发布新闻啦!

news = new News();
        //观察者
        user_one = new User("用户一",this);
        user_two = new User("用户二",this);
        user_three = new User("用户三",this);

news.addObserver(user_one);
news.addObserver(user_two);
news.addObserver(user_three);

news.postNewPublication("重大新闻,新的一期Android技术前线周报发布啦!!!");

Observer和Observable是JDK中的内置类型,可以看出观察者模式的重要性。在这里Observer抽象的观察者user是具体的观察者;Observable是抽象的主题,News是具体的主题。

当User订阅了News之后,当News有更新,就会去遍历所有观察者(User),然后给每一个观察者更新信息,即调用User的update()方法,这样就完成了一对多的通知功能。
在这个过程中,完全依赖Observer和Observable,对于User和News完全没有耦合,保证了订阅的灵活性和可扩展性。

三深入理解

3.1RecycleView的观察者模式

RecycleView最重要的一个功能就是Adapter,当我们向RecycleView中添加数据后,都会调用notifyDataSetChanged()方法,这是为什么呢,今天来一探究竟。
我们先看adapter.notifyDataSetChanged()函数

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
  //代码省略
  public static abstract class Adapter<VH extends ViewHolder> {

    private final AdapterDataObservable mObservable = new AdapterDataObservable();

    //代码省略

    public final void notifyDataSetChanged() {
      mObservable.notifyChanged();
    }

    //代码省略
  }

  //代码省略
  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();
        }
    }
    //代码省略
  }
}

从上面的代码我们可以看出来,当我们调用adapter.notifyDataSetChanged()时,实际在遍历所有的观察者,并调用他们的onChanged方法,从而告诉观察者发生了变化。
那么问题来了,这些观察者从哪里来的?
其实这些观察者是在setAdapter时候产生的。我们看下相关代码:

public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
  private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

  //代码省略
  public void setAdapter(Adapter adapter) {
    //代码省略
    setAdapterInternal(adapter, false, true);
    //代码省略
  }

  private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
          boolean removeAndRecycleViews) {
      /**
       *如果已经有了一个adapter,那么先注销该Adapter的对应的观察者
       */
      if (mAdapter != null) {
          mAdapter.unregisterAdapterDataObserver(mObserver);
          mAdapter.onDetachedFromRecyclerView(this);
      }

      //代码省略

      mAdapterHelper.reset();
      final Adapter oldAdapter = mAdapter;
      mAdapter = adapter;
      if (adapter != null) {

         /**
          * 将这个观察者注册到Adapter中,实际上是注册到AdapterDataObserver
          */
          adapter.registerAdapterDataObserver(mObserver);
          adapter.onAttachedToRecyclerView(this);
      }

      //代码省略
  }
  //代码省略
  private class RecyclerViewDataObserver extends AdapterDataObserver {
     /**
      * 上面说过调用Adapter的notifyDataSetChanged时调用所有观察者的onChange方法,核心就是在这实现的。
      * 重新设置Flag,然后重新跟新布局
      */
      @Override
      public void onChanged() {
          assertNotInLayoutOrScroll(null);
          if (mAdapter.hasStableIds()) {
              mState.mStructureChanged = true;
              setDataSetChangedAfterLayout();
          } else {
              mState.mStructureChanged = true;
              setDataSetChangedAfterLayout();
          }
          if (!mAdapterHelper.hasPendingUpdates()) {
              requestLayout();
          }
      }

      //代码省略
  }

  //代码省略
}

到这里我们知道,当RecycleView数据发生变化时,调用AdapternotifyDataSetChanged函数,该函数会调用AdapterDataObservablenotifyChanged函数,这个函数会调用所有观察者(RecyclerViewDataObserver)的onChanged方法,然后所有观察者会重新绘制布局。
这就是一个观察者模式。

扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~