一、概述
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
数据发生变化时,调用Adapter
的notifyDataSetChanged
函数,该函数会调用AdapterDataObservable
的notifyChanged
函数,这个函数会调用所有观察者(RecyclerViewDataObserver)的onChanged
方法,然后所有观察者会重新绘制布局。
这就是一个观察者模式。
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~