ListView – 观察者模式的应用
文章开头先贴下另外几篇设计模式的分析,感兴趣的可以看下:
设计模式-简单说明
ListView适配器模式的应用
AsyncTask模板模式的应用
ListView桥接模式的应用
我们先简单的了解一下观察者模式的含义:在我的理解中,观察者模式就是观察者对被观察者高度敏感,一旦被观察者状态发生变化,观察者马上根据新状态刷新自身。在java的观察者模式经典实现中,是通过在被观察者中注册观察者(简单来说就是被观察者持有所有观察者的引用),一旦被观察者状态发生变化,就遍历所有观察者,调用它们的刷新方法。
我们现在以ListView中观察者模式的应用为例,先放一张基于观察者模式的UML类图(新手,如果有错误,还望指出勿喷):
先简单的理一下关系,ListView持有一个ListAdapter引用,BaseAdapter为其实现类,我们是以使用BaseAdapter为例,所以这里你也可以理解为就是BaseAdapter,在BaseAdapter中定义有一个final类型的DataSetObservable的被观察者对象,当我们调用setAdapter时会调用DataSetObservable实例的注册观察者对象,这里的观察者对象可以理解为ListView(AdapterDataSetObserver为AbsListView的内部类),这样Adapter就成了被观察者,ListView变成了观察者(数据被观察,View观察)
现在我们进入源码详细分析一下具体实现,大家都知道ListView的经典桥段 mAdapter.notifyDataSetChange(),一旦调用了这个方法,就会刷新界面上的UI效果,那么它到底是怎么实现的呢,我们用的Adapter一般都是继承自BaseAdapter,notifyDataSetChanged()也是定义在BaseAdapter中的,我们来看一下BaseAdapter中相应的源码:
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();
}
...
}
在BaseAdapter中notifyDataSetChanged的实现看起来也没啥,就一句,我们看下mDataSetObservable的定义吧:
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
我们看见其实就两个方法:
- notifyChanged通知状态发生改变,观察者需要刷新状态
- notifyInvalidated通知被观察者数据无效作废
而mObservers则是它的基类定义的一个保存观察者的集合,我们简单过一下基类的定义:
public abstract class Observable<T> {
/**
* 观察者集合的定义
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* 注册观察者,就是添加到集合中管理
*/
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);
}
}
/**
* 注销观察者,从集合中移除
*/
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();
}
}
}
好了,被观察者就这么点东西,我们已经理清了,那我们该看看观察者在哪呢,它是什么时候被注册的,在通知刷新的时候又做了一些什么操作呢?我们使用ListView的时候设置适配器是必不可少的,适配器的功能除了适配数据成View之外,还注册了观察者,我们看下setAdapter的具体实现:
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
//这里是防止重复注册,所以会把之前的观察者先注销
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
...
//可以看见这里创建一个观察者,然后调用Adapter的注册方法。
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
现在观察者和被观察者已经联系起来了,我们看下,观察者的onChanged都做了些啥,从而实现UI的刷新。从上面我们看见观察者对象对AdapterDataSetObserver实例,我们看下AdapterDataSetObserver的onChanged方法做了什么:
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
在这里调用了父类的onChanged方法,看下AdapterDataSetObserver的父类AdapterView.AdapterDataSetObserver中onChanged都做了一些什么操作:
@Override
public void onChanged() {
...
//刷新界面
requestLayout();
}
在父类的onChanged方法中我们看见了观察者会调用熟悉的requestLayout方法来刷新界面.这样一条线就连起来了。
这篇文章关于观察者模式的分析就到这里了,如果有什么不足的地方,希望大家指出,多多交流。