1. 观察者模式的介绍
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
例如:我们在使用应用市场下载应用时,我们的通知栏会有下载进度显示,我们的详情页会有进度显示,我们的列表中也会有下载进度显示,这就是一个典型的观察者设计模式,多个观察者监听同一个下载进度。
2. 观察者模式的使用场景
- 事件的多级触发场景。
- 跨系统的消息交换场景。
3. 观察者模式的UML类图
UML角色介绍
Subject: 抽象主题角色,也就是被观察的角色,抽象主题角色的所有观察者对象的引用保存在一个集合里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSuject: 将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
Obser: 为所有具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。
ConcreteObserver: 具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。
4. 观察者模式的简单实现
(1)、 首先定义一个观察者接口:
public interface Observer {
/**
* 更新接口
*
* @param subject 传入主题对象,获取主题的状态信息
*/
public void update(Subject subject);
}
观察者接口里面只有一个方法update(Subject suject),当有更新时,将被观察者对象传进来,然后获取其状态信息。
(2)、接着定义具体的观察者,实现观察者接口:
public class ConcreteObserver implements Observer {
private ConcreteSubject concreteSubject;
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(Subject subject) {
concreteSubject = (ConcreteSubject) subject;
String state = concreteSubject.getState();
System.out.println(name + " 观察到: " + state);
}
}
具体观察者在收到更新后,在update方法里面做出具体的操作
(3)、然后,定义具体的被观察对象
public abstract class Subject {
//用来保存注册的观察者
private List<Observer> observers = new ArrayList<Observer>();
/**
* 注册观察者,将其加入到集合中
*
* @param observer
*/
public void registerObserver(Observer observer) {
if (!observers.contains(observer)) {
observers.add(observer);
}
}
/**
* 取消注册
*
* @param observer
*/
public void unrigisterObserver(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
/**
* 通知所有的观察者
*/
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
}
被观察者里有一个集合,用户存储所有的观察者对象的引用。
使用registerObserver方法添加到集合中,使用unregisterObserver方法将观察者对象从集合中移除。
最后调用notifyObservers方法遍历所有的观察者,调用它们的update方法,将被观察者自身对象传递进去。
具体的每个观察者在自己的update方法里面做出相应的行为。
(4)、具体的被观察者
public class ConcreteSubject extends Subject {
private String state;
public String getState() {
return state;
}
public void change(String newSate) {
this.state = newSate;
this.notifyObservers();
}
}
这个具体的观察者,其实在日常开发中,根本没有抽象观察者,都只有一个具体的被观察者,这里为了实现UML类图里面的对应关系,所以多了一个抽象的观察者。
5. 观察者模式在Android源码中
我们平时在更新ListView时,都会使用notifyDataSetChanged()方法来更新界面,其实这就是一个观察者设计模式。
我们先简单来说一下大致步骤。
(1)、首先我们会通过setAdapter来设置Adapter,在Adapter里面设置了一个观察者。观察者里面有一个changed()方法,onChanged()方法面会重新刷新布局。
(2)、当我们的数据发生变化时,会调用Adapter里面的notifyDataSetChanged()方法来更新数据,在notifyDataSetChanged()方法里面,最终会遍历所有的的观察者,并调用其changed()方法。
下面我们从源码的角色:
(1)、首先我们我们先找出观察者,从刚刚的分析中,我们知道setAdapter会设置一个观察者。所以我们从setAdapter入手。
public void setAdapter(ListAdapter adapter) {
//忽略以上代码
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
//这段代码就是观察者。
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
从以上代码我们可以看到,setAdapter里面我们看到如下代码:
mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver);
从名字我们不难猜测到这是一个观察者。我们继续点进去,在AbsListView中我们看到如下代码:
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();
}
}
}
我们发现AdapterDataSetObserver继承自AdapterView.AdapterDataSetObserver,继续跟进:
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();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
上述代码就是我们想要的观察者的庐山真面目。里面我们注意发现有onChanged()方法,这个就相当于UML类图的update()方法。
(2)、 接着我们找出被观察对象。
我们从BaseAdapter里面的notifyDataSetChanged()方法入手,我们看到如下代码:
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
同时我们可以看到如下代码:
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
从方法的名字可以看出,以上两个方法的作用是,注册观察者和反注册观察者。
我们继续跟进,看到如下代码:
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
//遍历所有的观察者,调用每个观察者的onChanged方法。
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
}
从上面的代码中我们可以看到,notifyChanged()方法遍历了所有的观察者,并调用了其onChanged()方法。
分析到这里,我们可以肯定的说,这就是一个典型的观察者设计模式。
最后画张图来总结一下,让调用更加清晰明了。
6. 观察者模式的Android开发
下面模拟我们在应用市场下载通过一个应用时,可以同时在通知栏,详情页面,ListView同时观察到同步的进度。模拟每隔一秒钟将下载进度发给所有的观察者。
(1)、首先定义一个观察者接口接口:Listener
public interface Listener {
//更新进度的方法
void update(int progress);
}
(2)、具体的观察者:DownLoadListener
public class DownListener implements Listener {
//观察者的名称
public String name;
public DownListener(String name) {
this.name = name;
}
//具体观察者在收到更新后所进行的操作
@Override
public void update(int progress) {
System.out.println(name + " ,下载进度: " + progress);
}
}
(3)、下载服务DownloadService。模拟下载服务主要用到了计时器工具类,Timer和TimerTask,每隔一秒钟,通过handler将进度发送出去。handler收到进度后,遍历所有的观察者,调用所有监听的观察者的update方法,将进度传递进去。
public class DownloadService {
private List<Listener> listeners = new ArrayList<Listener>();
private static final int DOWN = 1;
private int progress = 1;
private Timer mTimer;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DOWN:
//通知所有的观察者
notifyAllListeners(msg.arg1);
break;
default:
break;
}
}
};
public DownloadService() {
mTimer = new Timer();
}
//模拟开启服务
public void startService() {
setTimerTask();
}
//定时服务
private void setTimerTask() {
mTimer.schedule(new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.arg1 = ++progress;
message.what = DOWN;
handler.sendMessage(message);
}
}, 1000, 1000/* 表示1000毫秒之後,每隔1000毫秒執行一次 */);
}
//遍历通知所有的观察者
public void notifyAllListeners(int progress) {
for (Listener listener :
listeners) {
listener.update(progress);
}
}
//注册监听
public void registerService(Listener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
//取消监听
public void ungisterService(Listener listener) {
if (listeners.contains(listener)) {
listeners.remove(listener);
}
}
}
(4)、 测试代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//构造三个观察者
Listener listener1 = new DownListener("观察者1");
Listener listener2 = new DownListener("观察者2");
Listener listener3 = new DownListener("观察者3");
//下载服务
DownloadService downloadService = new DownloadService();
//注册监听者
downloadService.registerService(listener1);
downloadService.registerService(listener2);
downloadService.registerService(listener3);
//开启服务
downloadService.startService();
}
}
7. 总结
- 优点:
- 观察者与被观察者之间属于轻度的关联关系,两个之间是抽象呢耦合的。易于扩展。
- 缺点:
- 由于观察者模式是一条触发链,当观察者比较多的时候,靠能会导致执行效率下降,一个观察者的卡顿,会导致整整体的执行效率。