前言
Android的設計模式系列文章介紹,歡迎關注,持續更新中:
Android的設計模式-設計模式的六大原則
一句話總結23種設計模式則
創建型模式:
Android的設計模式-單例模式
Android的設計模式-建造者模式
Android的設計模式-工廠方法模式
Android的設計模式-簡單工廠模式
Android的設計模式-抽象工廠模式
Android的設計模式-原型模式
行為型模式:
Android的設計模式-策略模式
Android的設計模式-狀態模式
Android的設計模式-責任鏈模式
Android的設計模式-觀察者模式
Android的設計模式-模板方法模式
Android的設計模式-迭代器模式
Android的設計模式-備忘錄模式
Android的設計模式-訪問者模式
Android的設計模式-中介者模式
Android的設計模式-解釋器模式
Android的設計模式-命令模式
結構型模式:
Android的設計模式-代理模式
Android的設計模式-組合模式
Android的設計模式-適配器模式
Android的設計模式-裝飾者模式
Android的設計模式-享元模式
Android的設計模式-外觀模式
Android的設計模式-橋接模式
1.定義
定義對象間的一種一個對多的依賴關系,當一個對象的狀態發送改變時,所以依賴于它的對象都得到通知并被自動更新。
2.介紹
- 觀察者屬于行為型模式。
- 觀察者模式又被稱作發布/訂閱模式。
- 觀察者模式主要用來解耦,將被觀察者和觀察者解耦,讓他們之間沒有沒有依賴或者依賴關系很小。
3.UML類圖
角色說明:
- Subject(抽象主題):又叫抽象被觀察者,把所有觀察者對象的引用保存到一個集合里,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。
- ConcreteSubject(具體主題):又叫具體被觀察者,將有關狀態存入具體觀察者對象;在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。
- Observer (抽象觀察者):為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
- ConcrereObserver(具體觀察者):實現抽象觀察者定義的更新接口,當得到主題更改通知時更新自身的狀態。
4.實現
繼續以送快遞為例,快遞員有時只是把快遞拉到樓下,然后就通知收件人下樓去取快遞。
4.1 創建抽象觀察者
定義一個接到通知的更新方法,即收件人收到通知后的反應:
public interface Observer {//抽象觀察者
public void update(String message);//更新方法
}
4.2 創建具體觀察者
實現抽象觀察者中的方法,這里創建兩個類,一個男孩類和一個女孩類,定義他們收到通知后的反應:
public class Boy implements Observer {
private String name;//名字
public Boy(String name) {
this.name = name;
}
@Override
public void update(String message) {//男孩的具體反應
System.out.println(name + ",收到了信息:" + message+"屁顛顛的去取快遞.");
}
}
public class Girl implements Observer {
private String name;//名字
public Girl(String name) {
this.name = name;
}
@Override
public void update(String message) {//女孩的具體反應
System.out.println(name + ",收到了信息:" + message+"讓男朋友去取快遞~");
}
}
4.3 創建抽象主題
即抽象被觀察者,定義添加,刪除,通知等方法:
public interface Observable {//抽象被觀察者
void add(Observer observer);//添加觀察者
void remove(Observer observer);//刪除觀察者
void notify(String message);//通知觀察者
}
4.4 創建具體主題
即具體被觀察者,也就是快遞員,派送快遞時根據快遞信息來通知收件人讓其來取件:
public class Postman implements Observable{//快遞員
private List<Observer> personList = new ArrayList<Observer>();//保存收件人(觀察者)的信息
@Override
public void add(Observer observer) {//添加收件人
personList.add(observer);
}
@Override
public void remove(Observer observer) {//移除收件人
personList.remove(observer);
}
@Override
public void notify(String message) {//逐一通知收件人(觀察者)
for (Observer observer : personList) {
observer.update(message);
}
}
}
4.5 客戶端測試
public void test(){
Observable postman=new Postman();
Observer boy1=new Boy("路飛");
Observer boy2=new Boy("喬巴");
Observer girl1=new Girl("娜美");
postman.add(boy1);
postman.add(boy2);
postman.add(girl1);
postman.notify("快遞到了,請下樓領取.");
}
輸出結果:
路飛,收到了信息:快遞到了,請下樓領取.屁顛顛的去取快遞.
喬巴,收到了信息:快遞到了,請下樓領取.屁顛顛的去取快遞.
娜美,收到了信息:快遞到了,請下樓領取.讓男朋友去取快遞~
4.6 說明:
實際上,JDK內部也內置了Observable(抽象被觀察者),Observer(抽象觀察者)這兩個類,我們也可以直接拿來用,其代碼如下:
public interface Observer {//(抽象觀察者
//只定義了一個update方法
void update(Observable o, Object arg);
}
public class Observable {//抽象被觀察者
private boolean changed = false;//定義改變狀態,默認為false
private final ArrayList<Observer> observers;//定義一個觀察者list
public Observable() {//構造函數,初始化一個觀察者list來保存觀察者
observers = new ArrayList<>();
}
//添加觀察者,帶同步字段的,所以是線程安全的
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!observers.contains(o)) {
observers.add(o);
}
}
//刪除觀察者
public synchronized void deleteObserver(Observer o) {
observers.remove(o);
}
//通知所以觀察者,無參數
public void notifyObservers() {
notifyObservers(null);
}
//通知所有觀察者,帶參數
public void notifyObservers(Object arg) {
Observer[] arrLocal;
//加synchronized字段,保證多線程下操作沒有問題
synchronized (this) {
if (!hasChanged())//這里做了是否發生改變的判斷,是為了防止出現無意義的更新
return;
arrLocal = observers.toArray(new Observer[observers.size()]);//ArrayList轉換成一個臨時的數組,這樣就防止了通知,添加,移除同時發生可能導致的異常
clearChanged();///清除改變狀態,設置為false
}
//遍歷逐一通知
for (int i = arrLocal.length-1; i>=0; i--)
arrLocal[i].update(this, arg);
}
//清楚所有觀察者
public synchronized void deleteObservers() {
observers.clear();
}
//設置被觀察者為改變狀態,設置為true
protected synchronized void setChanged() {
changed = true;
}
//清除改變狀態,設置為false
protected synchronized void clearChanged() {
changed = false;
}
//返回當前的改變狀態
public synchronized boolean hasChanged() {
return changed;
}
//觀察者數量
public synchronized int countObservers() {
return observers.size();
}
}
5. 應用場景
- 當一個對象的改變需要通知其它對象改變時,而且它不知道具體有多少個對象有待改變時。
- 當一個對象必須通知其它對象,而它又不能假定其它對象是誰
- 跨系統的消息交換場景,如消息隊列、事件總線的處理機制。
6. 優點
- 解除觀察者與主題之間的耦合。讓耦合的雙方都依賴于抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。
- 易于擴展,對同一主題新增觀察者時無需修改原有代碼。
7. 缺點
- 依賴關系并未完全解除,抽象主題仍然依賴抽象觀察者。
- 使用觀察者模式時需要考慮一下開發效率和運行效率的問題,程序中包括一個被觀察者、多個觀察者,開發、調試等內容會比較復雜,而且在Java中消息的通知一般是順序執行,那么一個觀察者卡頓,會影響整體的執行效率,在這種情況下,一般會采用異步實現。
- 可能會引起多余的數據通知。
8. Android中的源碼分析
8.1 控件中Listener監聽方式
Android中我們遇到的最常用的觀察者模式就是各種控件的監聽,如下:
Button button = (Button) findViewById(R.id.button);
//注冊觀察者
button.setOnClickListener(new View.OnClickListener() {
//觀察者實現
@Override
public void onClick(View arg0) {
Log.d("test", "Click button ");
}
});
上面代碼中,button
就是具體的主題,也就是被觀察者;new
出來的View.OnClickListenerd
對象就是具體的觀察者;OnClickListener
實際上就是個接口,也就是抽象觀察者;通過setOnClickListener
把觀察者注冊到被觀察者中。
一旦button
捕獲的點擊事件,即狀態發生變化的時候,就會通過回調注冊的OnClickListener
觀察者的onClick
方法會來通知觀察者,Button
狀態發生變化。。
相關源碼分析:
public interface OnClickListener {//抽象觀察者
void onClick(View v);//只有onClick這個方法
}
//注冊觀察者
public void setOnClickListener(@Nullable View.OnClickListener l) {
if (!isClickable()) {
setClickable(true);//設置為可點擊
}
getListenerInfo().mOnClickListener = l;//把傳入的 OnClickListener 對象賦值給了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener對象的引用
}
ListenerInfo getListenerInfo() {//返回ListenerInfo對象,這里是一個單例模式
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
public boolean performClick() {//執行點擊事件
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);//執行onClick方法,li.mOnClickListener即OnClickListener對象
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
8.2 Adapter的notifyDataSetChanged()方法
當我們使用ListView
時,需要更新數據時我們就會調用Adapter
的notifyDataSetChanged()
方法,那么我們來看看notifyDataSetChanged()
的實現原理,這個方法是定義在BaseAdaper
中,具體代碼如下:
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
實際上就是使用了觀察者模式,BaseAdapter
就是具體的被觀察者。接下來看看 mDataSetObservable.notifyChanged()
的實現:
//數據集被觀察者
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
//遍歷所有觀察者,并調用他們的onChanged()方法
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
//其他代碼略
}
現在我們看到了有觀察者的影子,那么這些觀察者是從哪里來的呢?實際上這些觀察者是在ListView
通過setAdaper()
設置Adaper時產生的:
public class ListView extends AbsListView {
//其他代碼略
public void setAdapter(ListAdapter adapter) {
//如果已存在Adapter,先注銷該Adapter的觀察者
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//其他代碼略
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();//獲取Adapter中的數據的數量
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();//創建一個數據集觀察者
mAdapter.registerDataSetObserver(mDataSetObserver);//注冊觀察者
//其他代碼略
}
}
}
從上面的代碼可以看到,觀察者有了,那么這個觀察者主要是干什么的呢?
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();//調用父類的onChanged()方法
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
//其他代碼略
}
AdapterDataSetObserver
類中的onChanged()
方法沒看出啥,繼續看他父類的onChanged()
方法:
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
//觀察者的核心實現
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();//獲取Adapter中的數據的數量
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
//重新布局
requestLayout();
}
//其他代碼略
}
最終就是在AdapterDataSetObserver
這個類里面的onChanged()
方法中實現了布局的更新。
簡單總結:
- 當
ListView
的數據發生變化時,我們調用Adapter
的notifyDataSetChanged()
方法,這個方法又會調用所有觀察者(AdapterDataSetObserver
)的onChanged()
方法,onChanged()
方法又會調用requestLayout()
方法來重新進行布局。
8.3 BroadcastReceiver
BroadcastReceiver
作為Android的四大組件之一,實際上也是一個典型的觀察者模式.通過sendBroadcast
發送廣播時,只有注冊了相應的IntentFilter
的BroadcastReceiver
對象才會收到這個廣播信息,其onReceive
方法才會被調起.BroadcastReceiver
的代碼比較復雜,這里就不展開了.先挖個坑,后面也會出BroadcastReceiver
的相關源碼分析.
8.4 其他
另外,一些著名的第三方事件總線庫,比如RxJava、RxAndroid、EventBus、otto等等,也是使用了觀察者模式.有興趣的可以去看下他們的源碼.
相關文章閱讀
Android的設計模式-設計模式的六大原則
一句話總結23種設計模式則
創建型模式:
Android的設計模式-單例模式
Android的設計模式-建造者模式
Android的設計模式-工廠方法模式
Android的設計模式-簡單工廠模式
Android的設計模式-抽象工廠模式
Android的設計模式-原型模式
行為型模式:
Android的設計模式-策略模式
Android的設計模式-狀態模式
Android的設計模式-責任鏈模式
Android的設計模式-觀察者模式
Android的設計模式-模板方法模式
Android的設計模式-迭代器模式
Android的設計模式-備忘錄模式
Android的設計模式-訪問者模式
Android的設計模式-中介者模式
Android的設計模式-解釋器模式
Android的設計模式-命令模式
結構型模式:
Android的設計模式-代理模式
Android的設計模式-組合模式
Android的設計模式-適配器模式
Android的設計模式-裝飾者模式
Android的設計模式-享元模式
Android的設計模式-外觀模式
Android的設計模式-橋接模式