本篇博客分享一下Android中常用的设计模式——观察者模式。
从本篇博客开发,只要是分享的设计模式一类的,都尽量不再用术语,尽量直接用代码表示,简洁易懂。
对于观察者模式,我们只要记住一个关键点:解耦!解耦!解耦!
观察者示例
在JDK中,内置了观察者的相关类:Observer.java 和 Observable.java;
前者是:观察者需要继承的类;后者是:被观察者需要继承的类;
示例:前段时间我相中了一款键盘,但是刚好没货了,我把这个键盘加入了我的补货通知单中,也就是说:当有货的时候,淘宝会发送推送通知我补货成功的消息。 同理:很多人跟我一样都加入了补货通知单中,我们都会收到相同的消息通知。
这个示例中,观察者就是:我们, 被观察者就是:键盘
观察者:
/**
* FileName:PeopleObserver
* Create By:liumengqiang
* Description:观察者(关注断货商品的人)
*/
public class PeopleObserver implements Observer {
private String peopleName;
public PeopleObserver(String peopleName) {
this.peopleName = peopleName;
}
@Override
public void update(Observable o, Object arg) {
Log.e("PeopleObserver", "你好," + peopleName + ":你关注的" + arg + "到货啦,赶快下单吧");
}
}
被观察者:
/**
* FileName:TaobaoProductObservable
* Create By:liumengqiang
* Description:淘宝内的商品
*/
public class TaobaoProductObservable extends Observable {
public void noticeAllObserver(String goodsName) {
setChanged();
notifyObservers(goodsName);
}
}
将观察者和被观察者关联起来:
//**************Java下的观察者模式**************//
//创建观察者
PeopleObserver zhangsan = new PeopleObserver("张三");
PeopleObserver lisi = new PeopleObserver("李四");
PeopleObserver wangwu = new PeopleObserver("王五");
//创建被观察者
TaobaoProductObservable observable = new TaobaoProductObservable();
observable.addObserver(zhangsan);
observable.addObserver(lisi);
observable.addObserver(wangwu);
//通知观察者,货物到货了
observable.noticeAllObserver("阿米洛键盘");
这里我使用的JDK提供的内置观察者类。 观察者需要继承Observer ,然后重写update方法,在该方法内可以做相应的操作。 被观察者需要继承Observable,然后键盘补货成功,需要通知订阅该键盘的人的时候,需要做两个操作:
//设置标记为:被观察者已经改变的标记
setChanged();
//开始依次通知观察者。
notifyObservers(goodsName);
整体的简单示例就是如此简单,但是我们可能疑惑了,被观察者内部notifyObservers方法到底是怎么执行的呢? 怎么发送消息给观察者的呢?
观察者源码解析
在查看源码之前,我们先理一下从订阅到发布时间的步骤,以上一个示例为例:
- 创建观察者,继承自Observer
- 创建被观察者,继承自Observer
- 创建观察者和被观察者,然后通过addObserver方法,执行观察者订阅被观察者操作。
- 最后依次执行被观察者的:setChanged,notifyObservers方法,通知观察者数据改变。
上述四步就是整个观察者从创建到订阅再到发布的流程。
现在我们看下Observable的addObserver方法:
//同步,方法锁,防止并发
public synchronized void addObserver(Observer o) {
if (o == null) //空判断
throw new NullPointerException();
if (!obs.contains(o)) { //obs是个Vector对象
obs.addElement(o); //将观察者添加到obs集合中
}
}
首先addObserver方法是个线程安全的方法;在该方法内部,首先做了Observer对象空判断,最后将Observer添加到了abs集合中。
obs是一个Vector集合,Vector和ArrayList一样都是属于集合,里面都维护了一个数组,但是最大区别就是Vector是线程安全的,里面每一个方法,基本都是同步方法。 对Vector感兴趣的,可以自行查看源码学习。
通过上述代码我们得到一点:调用addObserver方法,将Observer对象添加到了集合中。
接下来分析发布事件源码:
// 置changed标记为:true
protected synchronized void setChanged() {
changed = true;
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
// 添加锁,使代码同步
synchronized (this) {
//判断数据是否改变了
if (!hasChanged())
return;
arrLocal = obs.toArray();
clearChanged();
}
//遍历复制的数组,然后调用每个Observer的update的方法。
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
// 获取changed标记,由于setChanged方法置为了 True,因此:此时返回true
public synchronized boolean hasChanged() {
return changed;
}
我们抛开其他的代码:只需要记住一个核心的东西,就是notifyObservers方法中遍历了Vector数组,然后调用了Observer的update的方法。
这里我实际上是有个问题的:为什么setChanged是个单独的方法呢? 为什么不把changed放在notifyObservers方法中呢? 知道的小伙伴可以分享一下,谢谢!
Android中观察者模式的应用
我们刚学习Android的适用,肯定用过ListView,ListView和BaseAdapter二者就是典型的应用了观察者模式。
如下两个方法肯定是我们闭着眼睛都能想到的:
- setAdapter:因为只有设置了setAdapter方法的ListView才能显示。
- notifyDataSetChanged:当数据源改变的时候,我们要刷新UI,此时需要调用notifySetChanged方法;
我们想先看下notifyDataSetChanged方法:
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
里面就一句话调用mDataSetObservable的notifyChanged方法,mDataSetObservable是一个DataSetObservable对象,DataSetObserva继承自Observable对象:
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
// 这里循环遍历mObservers集合,
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
....
}
在notifyChanged方法内,循环遍历了mObservers集合,执行Observer的onChanged方法, 那么mObservers集合添加数据是在父类:Observable中的:
public void registerObserver(T observer) {
......
mObservers.add(observer);
......
}
public void unregisterObserver(T observer) {
.....
mObservers.remove(index);
......
}
那么什么之后调用了registerObserver方法呢?是在BaseAdapter的registerDataSetObserver方法中:
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
那么Adapter的registerDataSetObserver方法又是什么时候调用的呢?换句话说:Observer是什么时候注册的呢?
答案是:在setAdapter方法中。
我们使用ListView控件,肯定是要最后调用setAdapter方法:
@Override
public void setAdapter(ListAdapter adapter) {
//创建了AdapterDataSetObserver对象
mDataSetObserver = new AdapterDataSetObserver();
//调用了adapter的registerDataSetObserver方法,将观察者注册到集合中.
mAdapter.registerDataSetObserver(mDataSetObserver);
// 重新测量绘制
requestLayout();
}
首先通过反推理,我们可以知道,其实AdapterDataSetObserver是一个继承自Observer的类,因为adapter的registerDataSetObserver方法接收到的参数类型是:Observer类型。
那么AdapterDataSetObserver是什么呢? 这个源码是在基类AbsListView中的。
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
.......
}
AdapterDataSetObserver又继承自AdapterView中的AdapterDataSetObserver 类:
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
.....
//重新测量绘制
requestLayout();
}
......
}
至此整个发布数据更新流程就通了。
总结:BaseAdapter内部有一个mDataSetObservable对象,而这个对象实际上就相当于一个:被观察者,而观察者就是setAdapter的时候新建的:mDataSetObserver对象,这个对象就是一个Observer对象,当数据源发生改变的时候,会通过遍历mDataSetObservable的内部数组,执行每一个mDataSetObserver的onChanged方法。
观察者模式的优缺点:
优点:
- 有点还是解耦了
缺点 - 一个被观察者,多个观察者,执行执行顺序问题,如果其中一个执行慢,那么就会直接影响之后的观察者代码运行