前言
Android的設(shè)計(jì)模式系列文章介紹,歡迎關(guān)注,持續(xù)更新中:
Android的設(shè)計(jì)模式-設(shè)計(jì)模式的六大原則
一句話總結(jié)23種設(shè)計(jì)模式則
創(chuàng)建型模式:
Android的設(shè)計(jì)模式-單例模式
Android的設(shè)計(jì)模式-建造者模式
Android的設(shè)計(jì)模式-工廠方法模式
Android的設(shè)計(jì)模式-簡(jiǎn)單工廠模式
Android的設(shè)計(jì)模式-抽象工廠模式
Android的設(shè)計(jì)模式-原型模式
行為型模式:
Android的設(shè)計(jì)模式-策略模式
Android的設(shè)計(jì)模式-狀態(tài)模式
Android的設(shè)計(jì)模式-責(zé)任鏈模式
Android的設(shè)計(jì)模式-觀察者模式
Android的設(shè)計(jì)模式-模板方法模式
Android的設(shè)計(jì)模式-迭代器模式
Android的設(shè)計(jì)模式-備忘錄模式
Android的設(shè)計(jì)模式-訪問者模式
Android的設(shè)計(jì)模式-中介者模式
Android的設(shè)計(jì)模式-解釋器模式
Android的設(shè)計(jì)模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計(jì)模式-代理模式
Android的設(shè)計(jì)模式-組合模式
Android的設(shè)計(jì)模式-適配器模式
Android的設(shè)計(jì)模式-裝飾者模式
Android的設(shè)計(jì)模式-享元模式
Android的設(shè)計(jì)模式-外觀模式
Android的設(shè)計(jì)模式-橋接模式
1.定義
定義對(duì)象間的一種一個(gè)對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)送改變時(shí),所以依賴于它的對(duì)象都得到通知并被自動(dòng)更新。
2.介紹
- 觀察者屬于行為型模式。
- 觀察者模式又被稱作發(fā)布/訂閱模式。
- 觀察者模式主要用來解耦,將被觀察者和觀察者解耦,讓他們之間沒有沒有依賴或者依賴關(guān)系很小。
3.UML類圖

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