1.简述
观察者模式是使用频率非常高的模式了,它定义了对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖他的对象都会收到通知,并自动刷新。
很明显,说到这里首先就想到了Android中的ListView或者RecyclerView,当数据变化之后,再调用Adapter的notifyDataSetChanged()
方法,列表的UI也就刷新了。其实就是使用了观察者模式。
- Observable:抽象被观察者,持有所有注册的观察者对象的引用,提供注册和反注册观察者对象的方法(也就是添加和移除)。
- ConcreteObservable:具体的被观察者,在内部状态发生改变时,给所有注册过的观察者发出通知
- Observer:抽象观察者,定义一个抽象的更新方法,再得到被观察者通知时更新自己
- ConcreteObserver:具体的观察者,实现了具体的更新方法
2.实现
Android开发技术周报,每周会更新一些内容,但是不知道具体的更新时间,又想第一时间阅读更新内容。难道要一直按住F5等它更新么?那估计F5烂了可能都没有更新。其实我们只需要简单的订阅一下就好,当有新的内容更新的时候,会发邮件到你订阅的邮箱中。
上述的例子,Android开发技术周报就是被观察者,我们这些订阅者就是观察者;既然分清角色了,就可以简单的实现这个例子了。
/** 观察者程序员 */
public class Coder implements Observer {
private final String name;
public Coder(String name) {
this.name = name;
}
@Override
public void update(String content) {
System.out.println("收到的更新内容为:" + content);
}
}
/** 被观察者,android开发者周报 */
public class AndroidWeekly extends Observable {
private ArrayList<Observer> observers = new ArrayList<>();
public void sunbscribe(Observer observer){
if(!observers.contains(observer)){
observers.add(observer);
}
}
public void unsubscribe(Observer observer){
observers.remove(observer);
}
@Override
public void notifyObservers(Object obj) {
observers.forEach(observer -> observer.update("数据更新了"));
}
}
通过AndroidWeekly
的sunbscribe()
和unsunbscribe()
进行订阅和取消订阅,当AndroidWeekly
有新内容发布的时候,调用notifyObservers()
方法,所有的订阅者就都能收到更新的通知了。
3.源码中的观察者模式
在java.util
包中内置了Observer
接口和Observable
类,同时Observable
类实现了注册和反注册等方法,使用起来方便很多。可见观察者模式是非常重要的.
public class Observable {
private boolean changed = false;
private Vector<java.util.Observer> obs;
public Observable() {
obs = new Vector<>();
}
//注册
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
//反注册
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
//反注册所有观察者
public synchronized void deleteObservers() {
obs.removeAllElements();
}
//通知更新
public void notifyObservers() {
notifyObservers(null);
}
//通知更新
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
Observer
接口则是比较简单的代码,update()
的参数中除了可以传递数据意外,还提供了被观察者的引用对象。
public interface Observer {
void update(Observable o, Object arg);
}
而在android中使用观察者模式的,最先想到的应该就是adapter.notifyDataSetChanged()
方法了吧!首先看看notifyDataSetChanged()
方法具体做了什么
//此处只给出了重要的相关代码
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()
就是直接调用DataSetObservable
的notifyChanged()
方法,我们看看DataSetObservable
到做了什么
public class DataSetObservable extends Observable<DataSetObserver> {
/**遍历Observer集合,调用每个观察者的onChanged方法,通知它们被观察者发生变化了*/
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
//省略代码...
}
代码还是很简单的,DataSetObservable
继承Observable
,Observable
中实现了注册和反注册相关的方法,这里就不贴代码了。
底层基本明白,让我们往上寻找观察者的来源,会发现在ListView
的setAdapter()
方法中,通过registerDataSetObserver()
方法注册了AdapterDataSetObserver
的对象。这里就清楚了,其实观察者就是AdapterDataSetObserver
,而AdapterDataSetObserver
又是继承了AbsListView
父类AdapterView
的AdapterDataSetObserver
。我们直接去看看它的onChanged()
方法到底做了些什么。
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调用notifyDataSetChanged()
方法,最终会调用上述的onChanged()
方法,使ListView刷新。
ListView的观察者模式,是保持单线程的,也就是UI线程,所以不存在跨线程的情况,而实际应用中,这一点我们无法保证。而在Android中也使用更加复杂的观察者模式,例如:
BroadcastReceiver
、EventBus
、Otto
等等
4.总结
观察者模式主要的作用就是对象解耦,将观察者和被观察者完全隔离,只依赖于Observer
和Observable
,使得原本对象处于松耦合,不会相互影响。
优点
- 观察者和被观察者之间是抽象耦合
- 增强系统灵活性,可扩展性
缺点
- 开发效率额和运行效率,程序中被观察者和观察者往往是一对多的关系,使得开发(调试)等内容会比较复杂,而在同一线程中时,会影响程序的整体效率(可以考虑使用异步解决,同时也需要确保异步不会出现问题)