观察者模式最常用的地方是GUI(图形用户界面)系统、订阅——发布系统。其重要作用就是将观察者和被观察者“解耦”,尽可能的降低两者之间的依赖性。
观察者模式定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。它是一种行为型模式,行为型模式主要是描述类或者对象是怎样交互和怎样分配职责的。
使用场景
- 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系;
- 事件多级触发场景;
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
角色介绍
- Subject:抽象主题,也就是被观察(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫具体被观察者(ConcreteObservable)角色。
- Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。
- ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。
观察者模式的简单实现
1.抽象观察者
public interface Observer {
void doSth(String msg); //接收通知的方法
}
2.具体观察者
/**
*男人
*/
public class Man implements Observer {
private String mName;
public Man(String mName) {
this.mName = mName;
}
@Override
public void doSth(String msg) { //接收通知后的具体实现方法
LogUtil.e("Observer--->", "这个男人:" + mName + msg + "的方式是:打了一套军体拳...");
}
}
/**
*女人
*/
public class WoMan implements Observer {
private String mName;
public WoMan(String mName) {
this.mName = mName;
}
@Override
public void doSth(String msg) {
LogUtil.e("Observer--->", "这个女人:" + mName + msg + "的方式是:闭着眼瞎挠...");
}
}
3.抽象被观察者
分别定义了“添加”、“移除”以及“通知”观察者的方法
public interface Observable {
void add(Observer observer);
void remove(Observer observer);
void notify(String string);
}
4.具体被观察者
public class SuperMan implements Observable {
//观察者集合
private List<Observer> mList = new ArrayList<>();
@Override
public void add(Observer observer) {
mList.add(observer);
}
@Override
public void remove(Observer observer) {
mList.remove(observer);
}
@Override
public void notify(String string) {
for (Observer observer1:mList){
observer1.doSth(string);
}
}
}
5.测试
...
...
public void test(){
SuperMan superMan = new SuperMan();
superMan.add(new Man("张三"));
superMan.add(new WoMan("Mary"));
superMan.add(new Observer() { //有没有感觉类似于new OnClickListener()
@Override
public void doSth(String msg) {
LogUtil.e("Observer--->", "让我们一起摇摆!!!");
}
});
superMan.notify("打坏蛋");
}
...
...
输出结果
这个男人:张三打坏蛋的方式是:打了一套军体拳...
这个女人:Mary打坏蛋的方式是:闭着眼瞎挠...
让我们一起摇摆!!!
以上就是观察者模式的一个小测试,下面我们来看看Android开发中使用到的观察者模式
1.控件的点击监听Listener
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO
}
});
button就是一个被观察者,new View.OnClickListener()就是一个观察者,OnClickListener是个接口,即抽象观察者。被观察者button通过setOnClickListener()把观察者<new View.OnClickListener()>添加到自己名下。
观察者<new View.OnClickListener()>通过观察button的状态变化来回调实现了OnClickListener接口下的onClick方法,从而做出相应的操作。感兴趣的同学可以去研究一下View的源码。
2.Adapter的notifyDataSetChanged()方法
咱们把鼠标放在adapter.notifyDataSetChanged();方法上,然后CTRL+鼠标左键点进去瞅一眼...
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//这个是具体的被观察者
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public boolean hasStableIds() {
return false;
}
//注册观察者
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
//解注观察者
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
* (通知已经依附的观察者们基础数据已经改变,并且任何反映数据集的View都应该刷新他自己)
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
观察者模式无疑,BaseAdapter是个被观察者。现在咱把鼠标放在mDataSetObservable.notifyChanged();方法上并重复上一步的操作来深挖一下这个通知:
/**
* A specialization of {@link Observable} for {@link DataSetObserver}
* that provides methods for sending notifications to a list of
* {@link DataSetObserver} objects.
*
* 译:为DataSetObserver提供给一个Object集合发送通知的方法的一个专业化的Observable
*/
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*
* 译:在每个Observer中调用DataSetObserver的onChanged方法。
* 当数据集的内容已更改时调用,接收者将在下一次查询数据集时获取新内容。
*/
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything,
// including
// 一旦onChanged()方法被执行,它可以做任何事,包括
// removing itself from {@link mObservers} - and that could cause problems if
// 从 mObservers移除自己,如果这个mObservers集合使用了一个迭代器会引发问题。
// 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--) { //遍历所有的观察者,调用onChanged()方法
mObservers.get(i).onChanged();
}
}
}
...
...
}
在上面的代码中我们发现了"mObservers"观察者,它在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.
* 在集合中添加一个观察者,这个观察者不能为空并且必须没有被注册
*/
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.
* 移除前一个注册的观察者,这个观察者不能为空并且必须是已经被注册的
*/
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);
}
}
/**
* 移除所有已注册的观察者
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
跟我们上面写的例子差不多,只不过方法内部添加了一些异常判断。
下面就看看观察者是如何被注册到被观察者中的:
public class ListView extends AbsListView {
...
...
public void setAdapter(ListAdapter adapter) {
//如果Adapter与其观察者已存在,先注销
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
...
...
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
//创建一个观察者
mDataSetObserver = new AdapterDataSetObserver();
//注册观察者
mAdapter.registerDataSetObserver(mDataSetObserver);
...
...
}
}
}
再让我们多看一眼这个观察者AdapterDataSetObserver里究竟干了些啥...
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
...
...
}
......就有一个简单的onChanged()方法的复写,好像啥也看不出来,那就试试从它爹那找找看吧......
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
//重新布局
requestLayout();
}
...
...
}
这下看到了onChanged()方法的精髓,前面呜里巴嗦一大堆,我不管,也看不见,我就看到了requestLayout();布局更新。
看了这么多源码可能会有一些疲劳,下面给大家简单做个总结:当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged方法,这个方法又会调用DataSetObservable的notifyChanged方法,这个方法又遍历了所有的观察者(AdapterDataSetObserver )并逐一调用其onChanged方法,在onChanged方法中又会调用ListView的requestLayout方法更新布局。
过程大致如下:
1.在setAdapter的时候会初始化一个AdapterDataSetObserver的实例,并注册到Adapter中,这是个观察者;
2.在数据发生改变时,我们会调用Adapter.notifyDataSetChanged方法,实际上是调用BaseAdapter中DataSetObservable的notifyChanged方法,该方法遍历所有的观察者并调用其onChanged方法;
3.在AdapterDataSetObserver的onChanged方法中会获取Adapter中数据集的新数量,然后调用ListView的requestLayout方法更新布局。
其他使用观察者模式的还有BroadcastReceiver,以及我们熟悉的第三方库EventBus、RxJava等,这里由于篇幅已经够长就不多做分析了,有机会单独拿出来学习一下里面的原理。