Android框架設計模式(三)——Observer Method

一、觀察者模式


在介紹觀察者模式之前,先補充兩個概念: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返回給觀察者;

發佈了54 篇原創文章 · 獲贊 44 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章