一、觀察者模式
在介紹觀察者模式之前,先補充兩個概念:IOC(控制反轉)、DIP(依賴倒置)。
什麼是控制反轉和依賴倒置?
依賴倒置(控制反轉),是框架設計的核心,因爲有了它們會產生框架,框架的核心就是把【不變】的留在框架層次,把【變化】的留在應用層次,然後兩個層次之間通過接口來實現溝通,降低耦合。它們兩者本質是同樣的,只是一個是從原則上面描述,一個是從方式上面描述。
依賴倒置:
- 高層次的模塊不應該依賴於低層次的模塊,兩者都應該依賴於抽象接口。
- 抽象接口不應該依賴於具體實現。而具體實現則應該依賴於抽象接口。
控制反轉:
高層框架不應該依賴於底層的實現,底層的實現應該依賴於高層的框
架,高層框架封裝不變的部分,把變化的部分留出接口讓底層實現,通
過接口來實現高層與底層之間的溝通。在這個溝通的過程中,框架是主
動的(即調用者),而底層的具體應用實現是被動的(被調用者)。
總體來說,依賴倒置與控制反轉都是一個意思:依賴於接口編程,將具體的對象之間的關係通過輕量型的接口來分離,框架層通過接口調用應用層的不同實現(此處即框架與應用層解耦),達到反向控制的目的。依賴倒置是原則,控制反轉是體現。
一般來說,IOC有兩種實現的方式:
(1)繼承( Inheritance)+卡隼函數(hook)——在 Template Pattern中使用
(2)委託( Delegation)+卡隼函數(hook)——在Observer模式中使用
什麼是觀察者模式?
定義:
百度百科:觀察者模式(有時又被稱爲發佈(publish )-訂閱(Subscribe)模式、模型-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件(通知者)管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
通俗舉例:
通俗來說,舉古代打仗爲例,敵軍爲數據集合(被觀察者),軍官爲視圖(觀察者)。那麼中間的通知者就是偵察兵,偵察兵在前線偵查敵情的變動,一有風吹草動就會通知軍官,然後軍官採取相應的方案。這裏軍官就是觀察者,偵察兵就是通知者(不同角度可能角色會轉換)。
UML圖:
適用場景:
當一個對象的改變需要通知其它對象的時候,同時他又不知道具體有多
需要相互依賴的實體對象解耦,讓他們共同依賴於抽象接口(類別),
這樣即使兩個具體對象有改動(只要接口沒有變),也不會影響到對
方。而且觀察者模式可以實現一個通知者,通知多個完全不同的觀察
者。
二、觀察者模式在Android框架中的應用
BaseAdapter適配器
Adapter(適配器),單獨本身就是一種模式。但是在Android中,它還有另一個身份————觀察者模式中的通知者。
Android裏面實現觀察者模式是基於組合,而不是繼承的。即,觀察者和通知者都是鑲嵌在ListView和Adapter中,這樣的好處是降低了宿主ListView、Adapter與Observer和Observerable的耦合性。
Android裏面ListView的觀察者模式的開始是下面經典代碼:
MyAdapter adapter = new MyAdapter();
listView.setAdapter(adapter);
我們來看看setAdapter中做了什麼事:
@Override
public void setAdapter(ListAdapter adapter) {
//解除之前的Observer
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//清除之前的緩存,重新更新視圖集合
resetList();
mRecycler.clear();
//獲取通知者引用(Adapter)
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
//在通知者方註冊觀察者,以便數據集改變的時候發出通知
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
//註冊觀察者
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
看了setAdapter源碼,裏面有兩個地方是觀察者模式使用的核心:
即:
//獲取通知者引用(Adapter)
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mDataSetObserver = new AdapterDataSetObserver();
//調用通知者的方法註冊觀察者
mAdapter.registerDataSetObserver(mDataSetObserver);
ListView獲取Adapter的通知者引用,然後通過Adapter.registerDataSetObserver(),讓通知者Adapter獲得ListView的觀察者接口,這樣就實現了雙向溝通的通道,綁定完成。
我們再來看通知者BaseAdapter的源碼
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();//通知者引用
public boolean hasStableIds() {
return false;
}
//註冊觀察者綁定
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
//解除觀察者綁定
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
/**
* 通知已註冊的觀察者,數據集已經改變。任何觀察者在收到該信息
* 之後必須更新自己。
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
從上面的源碼我們可以看到,BaseAdapter裏面有一個Observerable通知者對象。在ListView調用了setAdapter()方法後,就實現了通知者與觀察者的綁定,當Adapter裏面的數據集合改變時,容器Activity通過調用Adapter.notifyDataSetChanged()然後再通過Observerable.notifyChanged方法通知ListView數據集改變了,然後就會調用ListView中DataObserver的onChaned()方法更新界面。
我們來看看真正的Observer和Observerable接口的源碼。
- ListView中的AdapterDataSetObserver源碼:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
}
- Adapter中的DataSetObserverable源碼:
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* 通知觀察者數據集改變了
*/
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}
由於Android框架系統比較複雜,因此它並沒有採取Gof的實現接口的方式來實現觀察者模式,而是通過委託的方式(嵌套一個通知者、觀察者),委託通知者和觀察者來實現通知和接收通知的任務。
其他應用
觀察者模式在Android中的應用還有很多。
比如:
廣播機制中的BroadCast(通知者)和BroadReceiver(觀察者);
Service中的Service(通知者)和ServiceConnection(觀察者),當服務啓動成功後,就調用onBind()方法通知ServiceConnection服務已經啓動,通過 onServiceConnected()將Binder返回給觀察者;