博客主页

观察者模式最常用的地方是GUI系统、订阅-发布系统。因为这个模式的一个重要的作用就是解耦,将被观察者和观察者解耦,使得他们之间的依赖性更小,甚至做到毫无依赖。

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

使用场景:

  1. 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系
  2. 事件多级触发场景
  3. 跨系统的消息交换场景,如消息队列、事件总线的处理机制

UML类图

  1. Subject:抽象主题,也就是被观察(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以又任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象
  2. ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫做具体被观察者(ConcreteObservable)角色
  3. Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己
  4. ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态

简单实现

观察者

import java.util.Observable;
import java.util.Observer;

// 程序员是观察者
public class Coder implements Observer {
    public final String name;

    public Coder(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable observable, Object arg) {
        System.out.println("hi, " + name + ", DevTechFrontier 更新啦,内容:" + arg);
    }

    @Override
    public String toString() {
        return "Coder{" +
                "name='" + name + '\'' +
                '}';
    }
}

被观察者

import java.util.Observable;

// DevTechFrontier:即开发技术前线,这个网站是被观察者角色
// 当它有更新时所有的观察者(这里指程序员)都会接到相应的通知
public class DevTechFrontier extends Observable {
    public void postNewPublication(String content) {
        setChanged();
        notifyObservers(content);
    }
}

测试代码

// 测试代码
public class Test {
    public static void main(String[] args) {
        //被观察者角色
        DevTechFrontier devTechFrontier = new DevTechFrontier();

        // 观察者
        Coder coder1 = new Coder("coder-1");
        Coder coder2 = new Coder("coder-2");
        Coder coder3 = new Coder("coder-3");

        // 将观察者注册到可观察对象的观察者列表中
        devTechFrontier.addObserver(coder1);
        devTechFrontier.addObserver(coder2);
        devTechFrontier.addObserver(coder3);

        devTechFrontier.postNewPublication("新的一期开发技术前线周报发布啦!");
    }
}

Observable 和 Observer 是JDK中的内置类型,这里Observer是抽象的观察者角色,Coder扮演的是具体观察者的角色;Observable对应的是抽象主题角色,DevTechFrontier则是具体的主题角色。

Android源码分析

ListView是Android中最重要的控件之一,而ListView最重要的一个功能就是Adapter。通常在ListView添加数据后,都会调用Adapter的notifyDataSetChanged()方法。
接下来跟进这个方法notifyDataSetChanged,这个方法定义在BaseAdapter中

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    // ...省略

    // 数据集被观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    // 当数据变化时,通过所有观察者
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
}

BaseAdapter是一个观察者模式,那么它是如何运作的?这些观察者又是什么呢?

先到mDataSetObservable.notifyChanged()函数中看看:

public class DataSetObservable extends Observable<DataSetObserver> {
   
    // 调用每个观察者的onChanged函数来通知它们被观察者发生了变化
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
   
    // ... 省略
}

这段代码很简单,就是遍历所有观察者,并调用它们的onChanged方法,从而告知观察者发生了变化。

那么这些观察者是从哪里来的呢?其实这些观察者就是ListView通过setAdapter方法设置Adapter产生的,

// ListView.java

@Override
public void setAdapter(ListAdapter adapter) {
    // 如果已经有了一个Adapter,那么先注销该Adapter对应的观察者
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    // ...省略
    // AbsListView#setAdapter will update choice mode states.
    super.setAdapter(adapter);

    if (mAdapter != null) {
        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
        mOldItemCount = mItemCount;
        mItemCount = mAdapter.getCount();
        checkFocus();

        // 注意:创建一个观察者
        mDataSetObserver = new AdapterDataSetObserver();
        // 将这个观察者注册到Adapter中,实际上注册到DataSetObservable中
        mAdapter.registerDataSetObserver(mDataSetObserver);

    } 

    // ...省略

    requestLayout();
}

这段代码中,在设置Adapter时会构建一个AdapterDataSetObserver,这个就是上面所说的观察者,在将这个观察者注册到Adapter中。那AdapterDataSetObserver是如何运作的?它定义在ListView的父类AbsListView中,

// AbsListView.java

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
    @Override
    public void onChanged() {
        super.onChanged();
        if (mFastScroll != null) {
            mFastScroll.onSectionsChanged();
        }
    }

    @Override
    public void onInvalidated() {
        super.onInvalidated();
        if (mFastScroll != null) {
            mFastScroll.onSectionsChanged();
        }
    }
}

它又继承AbsListView的父类AdapterView的AdapterDataSetObserver,

// AdapterView.java

class AdapterDataSetObserver extends DataSetObserver {

    private Parcelable mInstanceState = null;
    // 调用Adapter的notifyDataSetChanged时会调用所有观察者的onChanged方法
    // 核心实现就在这里
    @Override
    public void onChanged() {
        mDataChanged = true;
        mOldItemCount = mItemCount;
        // 获取Adapter中的数据的数量
        mItemCount = getAdapter().getCount();

        // Detect the case where a cursor that was previously invalidated has
        // been repopulated with new data.
        if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                && mOldItemCount == 0 && mItemCount > 0) {
            AdapterView.this.onRestoreInstanceState(mInstanceState);
            mInstanceState = null;
        } else {
            rememberSyncState();
        }
        checkFocus();
        // 重新布局ListView,GridView等AdapterView组件
        requestLayout();
    }
    // ...省略

    public void clearSavedState() {
        mInstanceState = null;
    }
}

到此可知,当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数会调用所有观察者(AdapterDataSetChanged)的onChanged方法,在onChanged函数中又会调用ListView重新布局的函数使得ListView刷新界面。

如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)


小兵兵同学
56 声望23 粉丝

Android技术分享平台,每个工作日都有优质技术文章分享。从技术角度,分享生活工作的点滴。