-
Android開發中無處不在的設計模式――單例模式
-
Android開發中無處不在的設計模式――Builder模式
前面介紹了單例模式和Builder模式,有興趣的見上面兩個鏈接,這篇文章側重介紹1下視察者模式。先看下這個模式的定義。
定義對象間的1種1對多的依賴關系,當1個對象的狀態發送改變時,所有依賴于它的對象都能得到通知并被自動更新
還是那句話,定義常常是抽象的,要深入的理解定義,你需要自己動手實踐1下。
先來說幾個情形。
-
情形1
有1種短佩服務,比如天氣預報服務,1旦你定閱該服務,你只需按月付費,付完費后,每天1旦有天氣信息更新,它就會及時向你發送最新的天氣信息。
-
情形2
雜志的定閱,你只需向郵局定閱雜志,繳納1定的費用,當有新的雜志時,郵局會自動將雜志送至你預留的地址。
視察上面兩個情形,有1個共同點,就是我們無需每時每刻關注我們感興趣的東西,我們只需做的就是定閱感興趣的事物,比如天氣預報服務,雜志等,1旦我們定閱的事物產生變化,比如有新的天氣預報信息,新的雜志等,被定閱的事物就會即時通知到定閱者,即我們。而這些被定閱的事物可以具有多個定閱者,也就是1對多的關系。固然,嚴格意義上講,這個1對多可以包括1對1,由于1對1是1對多的特例,沒有特殊說明,本文的1對多包括了1對1。
現在你反過頭來看看視察者模式的定義,你是否是豁然開朗了。
然后我們看1下視察者模式的幾個重要組成。
-
視察者,我們稱它為Observer,有時候我們也稱它為定閱者,即Subscriber
-
被視察者,我們稱它為Observable,便可以被視察的東西,有時候還會稱之為主題,即Subject
至于視察者模式的具體實現,這里帶帶大家實現1下場景1,其實java中提供了Observable類和Observer接口供我們快速的實現該模式,但是為了加深印象,我們不使用這兩個類。
場景1中我們感興趣的事情是天氣預報,因而,我們應當定義1個Weather實體類。
public class Weather { private String description; public Weather(String description) { this.description = description;
} public String getDescription() { return description;
} public void setDescription(String description) { this.description = description;
}
@Override public String toString() { return "Weather{" + "description=" + description + + };
}
}
然后定義我們的被視察者,我們想要這個被視察者能夠通用,將其定義成泛型。內部應當暴露register和unregister方法供視察者定閱和取消定閱,至于視察者的保存,直接用ArrayList便可,另外,當有主題內容發送改變時,會即時通知視察者做出反應,因此應當暴露1個notifyObservers方法,以上方法的具體實現見以下代碼。
public class Observable{
ListmObservers = new ArrayList(); public void register(Observerobserver) { if (observer == null) { throw new NullPointerException("observer == null");
}
synchronized (this) { if (!mObservers.contains(observer))
mObservers.add(observer);
}
} public synchronized void unregister(Observerobserver) {
mObservers.remove(observer);
} public void notifyObservers(T data) { for (Observerobserver : mObservers) {
observer.onUpdate(this, data);
}
}
}
而我們的視察者,只需要實現1個視察者的接口Observer,該接口也是泛型的。其定義以下。
public interface Observer{ void onUpdate(Observableobservable,T data);
}
1旦定閱的主題發送變換就會回調該接口。
我們來使用1下,我們定義了1個天氣變換的主題,也就是被視察者,還有兩個視察者視察天氣變換,1旦變換了,就打印出天氣信息,注意1定要調用被視察者的register進行注冊,否則會收不到變換信息。而1旦不敢興趣了,直接調用unregister方法進行取消注冊便可
public class Main { public static void main(String [] args){
Observableobservable=new Observable();
Observerobserver1=new Observer() {
@Override public void onUpdate(Observableobservable, Weather data) {
System.out.println("視察者1:"+data.toString());
}
};
Observerobserver2=new Observer() {
@Override public void onUpdate(Observableobservable, Weather data) {
System.out.println("視察者2:"+data.toString());
}
};
observable.register(observer1);
observable.register(observer2);
Weather weather=new Weather("晴轉多云");
observable.notifyObservers(weather);
Weather weather1=new Weather("多云轉陰");
observable.notifyObservers(weather1);
observable.unregister(observer1);
Weather weather2=new Weather("臺風");
observable.notifyObservers(weather2);
}
}
最后的輸出結果也是沒有甚么問題的,以下
視察者1:Weather{description=’晴轉多云’}
視察者2:Weather{description=’晴轉多云’}
視察者1:Weather{description=’多云轉陰’}
視察者2:Weather{description=’多云轉陰’}
視察者2:Weather{description=’臺風’}
接下來我們看看視察者模式在android中的利用。我們從最簡單的開始。還記得我們為1個Button設置點擊事件的代碼嗎。
Button btn=new Button(this);
btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {
Log.e("TAG","click");
}
});
其實嚴格意義上講,這個最多算是回調,但是我們可以將其看成是1對1的視察者模式,即只有1個視察者。
其實只要是set系列的設置監聽器的方法最多都只能算回調,但是有1些監聽器式add進去的,這類就是視察者模式了,比如RecyclerView中的addOnScrollListener方法
private ListmScrollListeners; public void addOnScrollListener(OnScrollListener listener) { if (mScrollListeners == null) {
mScrollListeners = new ArrayList();
}
mScrollListeners.add(listener);
} public void removeOnScrollListener(OnScrollListener listener) { if (mScrollListeners != null) {
mScrollListeners.remove(listener);
}
} public void clearOnScrollListeners() { if (mScrollListeners != null) {
mScrollListeners.clear();
}
}
然后有轉動事件時便會觸發視察者進行方法回調
public abstract static class OnScrollListener { public void onScrollStateChanged(RecyclerView recyclerView, int newState){} public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
} void dispatchOnScrolled(int hresult, int vresult) { if (mScrollListeners != null) { for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
mScrollListeners.get(i).onScrolled(this, hresult, vresult);
}
}
} void dispatchOnScrollStateChanged(int state) { if (mScrollListeners != null) { for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
mScrollListeners.get(i).onScrollStateChanged(this, state);
}
}
}
類似的方法很多很多,都是add監聽器系列的方法,這里也不再舉例。
還有1個地方就是Android的廣播機制,其本質也是視察者模式,這里為了簡單方便,直接拿本地廣播的代碼說明,即LocalBroadcastManager。
我們平時使用本地廣播主要就是下面4個方法
LocalBroadcastManager localBroadcastManager=LocalBroadcastManager.getInstance(this) localBroadcastManager.registerReceiver(BroadcastReceiver receiver, IntentFilter filter) localBroadcastManager.unregisterReceiver(BroadcastReceiver receiver) localBroadcastManager.sendBroadcast(Intent intent)
調用registerReceiver方法注冊廣播,調用unregisterReceiver方法取消注冊,以后直接使用sendBroadcast發送廣播,發送廣播以后,注冊的廣播會收到對應的廣播信息,這就是典型的視察者模式。具體的源代碼這里也不貼。
android系統中的視察者模式還有很多很多,有興趣的自己去發掘,接下來我們看1下1些開源框架中的視察者模式。1說到開源框架,你首先想到的應當是EventBus。沒錯,EventBus也是基于視察者模式的。
視察者模式的3個典型方法它都具有,即注冊,取消注冊,發送事件
EventBus.getDefault().register(Object subscriber) EventBus.getDefault().unregister(Object subscriber) EventBus.getDefault().post(Object event)
內部源碼也不展開了。接下來看1下重量級的庫,它就是RxJava,由于學習曲線的峻峭,這個庫讓很多人望而止步。
創建1個被視察者
ObservablemyObservable = Observable.create(
new Observable.OnSubscribe() { @Override public void call(Subscriber sub) { sub.onNext("Hello, world!"); sub.onCompleted(); }
}
);
創建1個視察者,也就是定閱者
SubscribermySubscriber = new Subscriber() { @Override public void onNext(String s) { System.out.println(s); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { }
};
視察者進行事件的定閱
myObservable.subscribe(mySubscriber)
具體源碼也不展開,不過RxJava這個開源庫的源碼個人還是建議很值得去看1看的。
總之,在Android中視察者模式還是被用得很頻繁的。