本篇博客分享一下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方法。
觀察者模式的優缺點:
優點:
- 有點還是解耦了
缺點 - 一個被觀察者,多個觀察者,執行執行順序問題,如果其中一個執行慢,那麼就會直接影響之後的觀察者代碼運行