一、问题的提出
举例:
WeatherData是气象站获取数据的对象,通过他获取到数据后更新到不同的布告板上。
WeatherData有三个方法:getTemperature(), getHumidity(), getPressure()用于获取温度,湿度,气压值
当新数据获得后,可以调用WeatherData中的measurementsChanged()方法,更新到不同的布告板上。
错误的设计:
二、初步认识
首先看看报纸的订阅是如何进行的:
1、报社的业务是出版报纸
2、向某家报社订阅报纸,只要他们有新报纸出版,就给你送来,只要你是他们的订户,就会一直收到新报纸
3、当你不想再看报纸时,取消订阅,他们就不会再送新报纸了
4、只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅
出版者(被观察者Subject) + 订阅者(观察者Observe) = 观察者模式
观察者模式--在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
三、模式类图
四、优点
观察者模式提供了一种对象设计,让被观察者和观察者之间松耦合,他们可以交互,但是不需要知道彼此的细节,被观察者只需要知道观察者实现了Oberver接口,并不需要知道观察者的具体类是谁,做了什么细节操作。因为被观察者唯一依赖的是一个实现Observer接口的对象列表,所以可以随时增加观察者,也可以用新的观察者取代现有的观察者,删除观察者。当有新的观察者出现时,无需修改被观察者代码,被观察者只把消息发送给观察者,观察者如何处理,被观察者并不关心。
Tips:
松耦合的设计能让我们建立有弹性的OO系统,能应对变化,对象之间的相互依赖降到了最低。
五、解决最初气象站的问题
1、首先建立被观察者Subject接口,用于注册、删除、通知观察者
2、在WeatherData中实现被观察者Subject接口
3、建立布告板实现观察者Observer接口(有多个布告板(观察者),实现的方法类似)
六、观察者模式在android中的应用
1、通常在ListView的内容变化时,会调用notifyDataSetChanged()这个方法,然后ListView里面的数据就会进行更新。感觉像是观察者模式,具体是不是再看看源码。
package android.widget;
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
......
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
......
}
这是一个观察者模式,里面提供了注册和注销观察者以及通知观察者的方法。
这些方法是通过DataSetObservable这个类调用的:
package android.database;public class DataSetObservable extends Observable {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
......
}
这个类继承自Observable,Observable中有一个protected final ArrayList
在notifyChanged方法中,循环这个集合,调用每一个观察者的onChanged()方法。
那么ListView和Adapter什么时候成了订阅关系。
在ListView的setAdapter()中
public class ListView extends AbsListView {
public void setAdapter(ListAdapter adapter) {
//如果已经有了一个adapter,注销这个adapter之前的观察者,
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
......
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
//将新的adapter赋给mAdapter
mAdapter = adapter;
}
......
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
//保存之前的数据个数
mOldItemCount = mItemCount;
//获取新的个数
mItemCount = mAdapter.getCount();
checkFocus();
//创建数据集观察者
mDataSetObserver = new AdapterDataSetObserver();
//注册观察者
mAdapter.registerDataSetObserver(mDataSetObserver);
...
}
} else {
...
}
requestLayout();
}
}
AdapterDataSetObserver是ListView的父类AbsListView的内部类
package android.widget;
public abstract class AbsListView extends AdapterView implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
ViewTreeObserver.OnTouchModeChangeListener,
RemoteViewsAdapter.RemoteAdapterConnectionCallback {
class AdapterDataSetObserver extends AdapterView.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
······
}
}
AdapterDataSetObserver是AdapterView.AdapterDataSetObserver的子类,所以要看super.onChanged()
package android.widget;
public abstract class AdapterView extends ViewGroup {
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
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();
//重新布局
requestLayout();
}
......
}
}
整理一下:当ListView数据变化时,调用Adapter的notifyDataSetChange方法,这个方法调用DataSetObservable的notifyChanged方法,这个方法又会调用所有观察者的onChanged方法,onChanged再调用重新布局View的方法,完成刷新数据的功能。