一配乱、什么叫觀察者?
先理解一個簡單的例子:很多購房者都在關(guān)注房子的價格變化于毙,每當房子的價格變化時歉糜,所有購房者都可以觀察得到,實際上購房者都屬于觀察者望众。
- 觀察者模式的定義:定義對象間一種一對多的依賴關(guān)系,使得每當一個對象發(fā)生改變伞辛,則所有依賴她的對象都會等到通知并被自動更新烂翰。
- 使用場景:1、 關(guān)聯(lián)行為場景;2蚤氏、事件多級觸發(fā)場景甘耿;3、跨系統(tǒng)的消息交互場景竿滨,如消息隊列佳恬、事件總線的處理機制
二、觀察者模式實現(xiàn)
在Java.util包中提供了Observable和Observer接口于游,使用它們即可完成觀察者模式毁葱。
Observable:需要被觀察的類必須繼承Observable類。Observable類的常用方法有:
- 1贰剥、public void addObserver(Observer o) : 添加一個觀察者倾剿;
- 2、public void deleteObserver(Observer o):刪除一個觀察者蚌成;
- 3前痘、public void setChanged() ; 被觀察者狀態(tài)發(fā)生改變凛捏;
- 4、public void notifyObservers(Object args) ; 通知所有觀察者狀態(tài)改變芹缔;
Observer:每個觀察者都需要實現(xiàn)Observer接口坯癣,Observer接口定義如下:
public interface Observer {
/**
*當被觀察者發(fā)生變化,并調(diào)用了notifyObserver方法最欠,就調(diào)用該方法
* @param o 被觀察者
* @param args 修改的內(nèi)容
*/
void update(Observable o, Object arg);
}
例:
package sl.com.designmodedemo.observer;
import java.util.Observable;
import java.util.Observer;
/**
* 定義房子為被觀察者
*/
class House extends Observable{
private float price ; //定義房子價格
public House(float price) {
this.price = price;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
setChanged(); //通知變化點
notifyObservers(price); //通知所有觀察者價格改變
}
@Override
public String toString() {
return "房子的價格是:" + this.price;
}
}
/**
* 房子價格變化觀察者
*/
class HousePriceObserver implements Observer{
private String name ; //觀察房子價格變化的人名
public HousePriceObserver(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
if (arg instanceof Float){ //判斷參數(shù)類型
System.out.println(this.name + " 觀察到的價格更改為:" + ((float)arg));
}
}
}
/**
* 測試
*/
public class Test {
public static void main(String args[]){
House house = new House(1000000.0F);
HousePriceObserver observer1 = new HousePriceObserver("觀察者A");
HousePriceObserver observer2 = new HousePriceObserver("觀察者B");
HousePriceObserver observer3 = new HousePriceObserver("觀察者C");
//加入觀察者
house.addObserver(observer1);
house.addObserver(observer2);
house.addObserver(observer3);
//輸出房子價格
System.out.println(house);
//修改房子價格
house.setPrice(2000000.0F);
System.out.println(house);
}
}
程序運行結(jié)果為:
房子的價格是:1000000.0
觀察者C 觀察到的價格更改為:2000000.0
觀察者B 觀察到的價格更改為:2000000.0
觀察者A 觀察到的價格更改為:2000000.0
房子的價格是:2000000.0
從程序運行結(jié)果可以發(fā)現(xiàn)示罗,多個觀察者都在觀察著價格的變化,當被觀察者房子價格一變化窒所,則所有觀察者都會知道鹉勒;、
三吵取、Android 源碼分析
ListView是Android中重要控件之一禽额,而ListView有一個Adapter,通常我們在給ListView的數(shù)據(jù)進行操作變化后皮官,都會調(diào)用Adapter的notifyDataSetChanged()方法脯倒,這是為什么呢?下面我們從源碼解析:
第一步捺氢,跟進notifyDataSetChanged()方法藻丢,這個方法定義在android.widget的BaseAdapter中,具體代碼如下:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//數(shù)據(jù)集觀察者
private final DataSetObservable mDataSetObservable = new DataSetObservable();
//代碼省略
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
//當數(shù)據(jù)集變化時摄乒,通知所有觀察者
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
}
從這里應該看出來BaseAdapter就是一個觀察者模式悠反,那么BaseAdapter是如何運作的?這些觀察者又是什么馍佑?下面接著一步一步的分析斋否。
現(xiàn)在先到mDataSetObservalbe.notifyChanged()的源碼看看:
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* 調(diào)用每個觀察者的onChanged函數(shù)來通知他們被觀察者發(fā)生了變化
*/
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
//代碼省略
}
這個代碼很簡單,就是在mDataSetObservable.notifyChanged()中遍歷所有的觀察者拭荤,并且調(diào)用他的onChanged()方法茵臭,從而告知觀察者發(fā)生了變化。
那么 這些觀察者從哪里來的呢舅世?其實這些觀察者是就是ListView在setAdapter方法設置Adapter產(chǎn)生的旦委,我們看相關(guān)代碼:
@Override
public void setAdapter(ListAdapter adapter) {
//如果已經(jīng)有了一個Adapter,并且mDataSetObserver不為空
if (mAdapter != null && mDataSetObserver != null) {
//先注銷該Adapter的觀察者
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
//代碼省略
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
//獲取數(shù)據(jù)的數(shù)量
mItemCount = mAdapter.getCount();
checkFocus();
//注意這里:創(chuàng)建了一個數(shù)據(jù)觀察者
mDataSetObserver = new AdapterDataSetObserver();
//將這個觀察者注冊到Adapter中雏亚,實際上是注冊到DataSetObservable中
mAdapter.registerDataSetObserver(mDataSetObserver);
//代碼省略
} else {
//代碼省略
}
requestLayout();
}
從程序可以看到缨硝,在設置Adapter的時會構(gòu)建一個AdapterDataSetObserver,這就是上面說的觀察者评凝,最后將這個觀察者注冊到了Adapter追葡,這樣我們的被觀察者、觀察者就都有了;
那么AdapterDataSetObserver是什么宜肉?它如何運作匀钧?那么現(xiàn)在來看看,AdapterDataObserver定義在ListView的父類AbsListView中谬返,具體代碼如下:
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();
}
}
}
它又繼承自AbsListView的父類AdapterView的AdapterDataSetObserver之斯,具體代碼如下:
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
//調(diào)用Adapter的notifyDataSetChanged的時會調(diào)用所有觀察者的onChanged()方法,核心就是這里
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();
}
//代碼省略
public void clearSavedState() {
mInstanceState = null;
}
}
到這里就知道了遣铝,當ListView的數(shù)據(jù)發(fā)生變化時佑刷,調(diào)用Adapter的notifyDataSetChanged(),這個方法又調(diào)用了DataSetObservable的notifyChanged()方法,這個方法會調(diào)用所有觀察者AdapterDataSetObserver的onChanged(),在onChanged函數(shù)中又會調(diào)用ListView重新布局的函數(shù)和使得ListView刷新界面酿炸,這就是一個觀察者模式瘫絮;
現(xiàn)在再整理一下這個過程,AdapterView中又一個內(nèi)部類AdapterDataSetObserver,在ListView設置Adapter時會構(gòu)建一個AdapterDataSetObserver填硕,并且注冊到Adapter中麦萤,這就是一個觀察者。而Adapter中有一個數(shù)據(jù)集被觀察者DataSetObservable扁眯,在數(shù)據(jù)數(shù)量發(fā)生變更時壮莹,開發(fā)者手動調(diào)用Adapter.notifyDataSetChanged(),而notifyDataSetChanged()實際上是調(diào)用DataObservable的notifyChanged()方法,該方法會遍歷所有的觀察者的onChanged()姻檀。在AdapterDataSetObserver的onChanged()會獲取Adpaer中數(shù)據(jù)集的新數(shù)量命满,然后調(diào)用ListView的requestLayout()方法重新進行布局,更新用戶界面绣版。