Android的設(shè)計(jì)模式-觀察者模式

前言

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類圖

觀察者模式UML類圖 .jpg
角色說明:
  • 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)用AdapternotifyDataSetChanged()方法,那么我們來看看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)用AdapternotifyDataSetChanged()方法,這個(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)的IntentFilterBroadcastReceiver對(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ì)模式-橋接模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容