Android源碼設計模式——觀察者模式

本篇博客分享一下Android中常用的設計模式——觀察者模式。
從本篇博客開發,只要是分享的設計模式一類的,都儘量不再用術語,儘量直接用代碼表示,簡潔易懂。

對於觀察者模式,我們只要記住一個關鍵點:解耦!解耦!解耦!

觀察者示例

在JDK中,內置了觀察者的相關類:Observer.java 和 Observable.java;

前者是:觀察者需要繼承的類;後者是:被觀察者需要繼承的類;

示例:前段時間我相中了一款鍵盤,但是剛好沒貨了,我把這個鍵盤加入了我的補貨通知單中,也就是說:當有貨的時候,淘寶會發送推送通知我補貨成功的消息。 同理:很多人跟我一樣都加入了補貨通知單中,我們都會收到相同的消息通知。

這個示例中,觀察者就是:我們, 被觀察者就是:鍵盤

觀察者:

/**
 * FileName:PeopleObserver
 * Create By:liumengqiang
 * Description:觀察者(關注斷貨商品的人)
 */
public class PeopleObserver implements Observer {
    private String peopleName;

    public PeopleObserver(String peopleName) {
        this.peopleName = peopleName;
    }

    @Override
    public void update(Observable o, Object arg) {
        Log.e("PeopleObserver", "你好," + peopleName + ":你關注的" + arg + "到貨啦,趕快下單吧");
    }
}

被觀察者:

/**
 * FileName:TaobaoProductObservable
 * Create By:liumengqiang
 * Description:淘寶內的商品
 */
public class TaobaoProductObservable extends Observable {
    public void noticeAllObserver(String goodsName) {
        setChanged();

        notifyObservers(goodsName);
    }
}

將觀察者和被觀察者關聯起來:

        //**************Java下的觀察者模式**************//
        //創建觀察者
        PeopleObserver zhangsan = new PeopleObserver("張三");
        PeopleObserver lisi = new PeopleObserver("李四");
        PeopleObserver wangwu = new PeopleObserver("王五");

        //創建被觀察者
        TaobaoProductObservable observable = new TaobaoProductObservable();
        observable.addObserver(zhangsan);
        observable.addObserver(lisi);
        observable.addObserver(wangwu);

        //通知觀察者,貨物到貨了
        observable.noticeAllObserver("阿米洛鍵盤");

在這裏插入圖片描述

這裏我使用的JDK提供的內置觀察者類。 觀察者需要繼承Observer ,然後重寫update方法,在該方法內可以做相應的操作。 被觀察者需要繼承Observable,然後鍵盤補貨成功,需要通知訂閱該鍵盤的人的時候,需要做兩個操作:

		//設置標記爲:被觀察者已經改變的標記
        setChanged();
        //開始依次通知觀察者。
        notifyObservers(goodsName);

整體的簡單示例就是如此簡單,但是我們可能疑惑了,被觀察者內部notifyObservers方法到底是怎麼執行的呢? 怎麼發送消息給觀察者的呢?

觀察者源碼解析

在查看源碼之前,我們先理一下從訂閱到發佈時間的步驟,以上一個示例爲例:

  1. 創建觀察者,繼承自Observer
  2. 創建被觀察者,繼承自Observer
  3. 創建觀察者和被觀察者,然後通過addObserver方法,執行觀察者訂閱被觀察者操作。
  4. 最後依次執行被觀察者的:setChanged,notifyObservers方法,通知觀察者數據改變。

上述四步就是整個觀察者從創建到訂閱再到發佈的流程。

現在我們看下Observable的addObserver方法:

	//同步,方法鎖,防止併發
    public synchronized void addObserver(Observer o) {
        if (o == null) //空判斷
            throw new NullPointerException();
        if (!obs.contains(o)) { //obs是個Vector對象
            obs.addElement(o); //將觀察者添加到obs集合中
        }
    }

首先addObserver方法是個線程安全的方法;在該方法內部,首先做了Observer對象空判斷,最後將Observer添加到了abs集合中。

obs是一個Vector集合,Vector和ArrayList一樣都是屬於集合,裏面都維護了一個數組,但是最大區別就是Vector是線程安全的,裏面每一個方法,基本都是同步方法。 對Vector感興趣的,可以自行查看源碼學習。

通過上述代碼我們得到一點:調用addObserver方法,將Observer對象添加到了集合中。

接下來分析發佈事件源碼:

	// 置changed標記爲:true
    protected synchronized void setChanged() {
        changed = true;
    }
    public void notifyObservers(Object arg) {
    
        Object[] arrLocal;
        // 添加鎖,使代碼同步
        synchronized (this) {
        	//判斷數據是否改變了
            if (!hasChanged())
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        //遍歷複製的數組,然後調用每個Observer的update的方法。
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    // 獲取changed標記,由於setChanged方法置爲了 True,因此:此時返回true
    public synchronized boolean hasChanged() {
        return changed;
    }

我們拋開其他的代碼:只需要記住一個核心的東西,就是notifyObservers方法中遍歷了Vector數組,然後調用了Observer的update的方法。

這裏我實際上是有個問題的:爲什麼setChanged是個單獨的方法呢? 爲什麼不把changed放在notifyObservers方法中呢? 知道的小夥伴可以分享一下,謝謝!

Android中觀察者模式的應用

我們剛學習Android的適用,肯定用過ListView,ListView和BaseAdapter二者就是典型的應用了觀察者模式。

如下兩個方法肯定是我們閉着眼睛都能想到的:

  1. setAdapter:因爲只有設置了setAdapter方法的ListView才能顯示。
  2. notifyDataSetChanged:當數據源改變的時候,我們要刷新UI,此時需要調用notifySetChanged方法;

我們想先看下notifyDataSetChanged方法:

    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

裏面就一句話調用mDataSetObservable的notifyChanged方法,mDataSetObservable是一個DataSetObservable對象,DataSetObserva繼承自Observable對象:

public class DataSetObservable extends Observable<DataSetObserver> {
    public void notifyChanged() {
        synchronized(mObservers) {
            // 這裏循環遍歷mObservers集合,
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
   ....
}

在notifyChanged方法內,循環遍歷了mObservers集合,執行Observer的onChanged方法, 那麼mObservers集合添加數據是在父類:Observable中的:

   public void registerObserver(T observer) {
       ......
       mObservers.add(observer);
       ......
    }

    public void unregisterObserver(T observer) {
      .....
      mObservers.remove(index);
      ......
    }

那麼什麼之後調用了registerObserver方法呢?是在BaseAdapter的registerDataSetObserver方法中:

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

那麼Adapter的registerDataSetObserver方法又是什麼時候調用的呢?換句話說:Observer是什麼時候註冊的呢?

答案是:在setAdapter方法中。

我們使用ListView控件,肯定是要最後調用setAdapter方法:

    @Override
    public void setAdapter(ListAdapter adapter) {
      		 //創建了AdapterDataSetObserver對象
            mDataSetObserver = new AdapterDataSetObserver();
            //調用了adapter的registerDataSetObserver方法,將觀察者註冊到集合中.
            mAdapter.registerDataSetObserver(mDataSetObserver);
		// 重新測量繪製
        requestLayout();
    }

首先通過反推理,我們可以知道,其實AdapterDataSetObserver是一個繼承自Observer的類,因爲adapter的registerDataSetObserver方法接收到的參數類型是:Observer類型。

那麼AdapterDataSetObserver是什麼呢? 這個源碼是在基類AbsListView中的。

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        .......
    }

AdapterDataSetObserver又繼承自AdapterView中的AdapterDataSetObserver 類:

   class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;

        @Override
        public void onChanged() {
            .....
            //重新測量繪製
            requestLayout();
        }
        ......
    }

至此整個發佈數據更新流程就通了。

總結:BaseAdapter內部有一個mDataSetObservable對象,而這個對象實際上就相當於一個:被觀察者,而觀察者就是setAdapter的時候新建的:mDataSetObserver對象,這個對象就是一個Observer對象,當數據源發生改變的時候,會通過遍歷mDataSetObservable的內部數組,執行每一個mDataSetObserver的onChanged方法。

觀察者模式的優缺點:

優點:

  • 有點還是解耦了
    缺點
  • 一個被觀察者,多個觀察者,執行執行順序問題,如果其中一個執行慢,那麼就會直接影響之後的觀察者代碼運行
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章