Android 設計模式 之 觀察者模式

來源:互聯網
上載者:User
/* * 觀察者模式 * 定義對象間的一種一個(Subject)對多(Observer)的依賴關係,當一個對象的狀態發送改變時,所以依賴於它的 * 對象都得到通知並被自動更新 *  * 當然,MVC只是Observer模式的一個執行個體。Observer模式要解決的問題為: * 建立一個一(Subject)對多(Observer)的依賴關係,並且做到當“一”變化的時候, * 依賴這個“一”的多也能夠同步改變。最常見的一個例子就是:對同一組資料進行統計分析時候, * 我們希望能夠提供多種形式的表示(例如以表格進行統計顯示、柱狀圖統計顯示、百分比統計顯示等)。 * 這些表示都依賴於同一組資料,我們當然需要當資料改變的時候,所有的統計的顯示都能夠同時改變。 * Observer模式就是解決了這一個問題。 *  * 適用性: * 1. 當一個抽象模型有兩個方面,其中一個方面依賴於另一方面 * 將這兩者封裝成獨立的對象中以使它們可以各自獨立的改變和服用 *  * 2. 當對一個對象的改變需要同時改變其他對象,而不知道具體有多少對象有待改變 *  * 3. 當一個對象必須通知其它對象,而它又不能假定其它對象是誰 *  * 參與者: * 1. Subject(目標) * 目標知道它的觀察者,可以有任意多個觀察者觀察同一個目標 * 提供註冊和刪除觀察者對象的介面 *  * 2. Observer(觀察者) * 為那些在目標發生改變時需獲得通知的對象定義個更新的介面 *  * 3. ConcreteSubject(具體目標) * 將有關狀態存入各ConcreteObserver對象 * 當它的狀態發送改變時,向它的各個觀察者發出通知 *  * 4. ConcreteObserver(具體觀察者) * 維護一個指向ConcreteObserver對象的引用 * 儲存有關狀態,這些狀態應與目標的狀態保持一致 * 實現Observer的更新介面是自身狀態與目標的狀態保持一致 *  *  * */

有空我將把UML圖補上。

下面看看Android使用到的觀察者模式.

觀察者(DataSetObserver),目標(Observable<T>),具體目標(DataSetObserverable)

Observer(觀察者),DataSetObserver抽象2個方法,一個是觀察資料改變的方法,一個是觀察資料變成無效(或者不可用)時的方法。

源碼路徑:framework/base/core/java/android/database/DataSetObserver.java

package android.database;/** * Receives call backs when a data set has been changed, or made invalid. The typically data sets * that are observed are {@link Cursor}s or {@link android.widget.Adapter}s. * DataSetObserver must be implemented by objects which are added to a DataSetObservable. */public abstract class DataSetObserver {    /**     * This method is called when the entire data set has changed,     * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.     */    public void onChanged() {        // Do nothing    }    /**     * This method is called when the entire data becomes invalid,     * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a     * {@link Cursor}.     */    public void onInvalidated() {        // Do nothing    }}

Subject(目標),Observable<T>是一個泛型的抽象類別,主要功能是註冊和撤銷observer。

源碼路徑:framework/base/core/java/android/database/Observable.java

package android.database;import java.util.ArrayList;/** * Provides methods for (un)registering arbitrary observers in an ArrayList. */public abstract class Observable<T> {    /**     * The list of observers.  An observer can be in the list at most     * once and will never be null.     */    protected final ArrayList<T> mObservers = new ArrayList<T>();    /**     * Adds an observer to the list. The observer cannot be null and it must not already     * be registered.     * @param observer the observer to register     * @throws IllegalArgumentException the observer is null     * @throws IllegalStateException the observer is already registered     */    public void registerObserver(T observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized(mObservers) {            if (mObservers.contains(observer)) {                throw new IllegalStateException("Observer " + observer + " is already registered.");            }            mObservers.add(observer);        }    }    /**     * Removes a previously registered observer. The observer must not be null and it     * must already have been registered.     * @param observer the observer to unregister     * @throws IllegalArgumentException the observer is null     * @throws IllegalStateException the observer is not yet registered     */    public void unregisterObserver(T observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized(mObservers) {            int index = mObservers.indexOf(observer);            if (index == -1) {                throw new IllegalStateException("Observer " + observer + " was not registered.");            }            mObservers.remove(index);        }    }        /**     * Remove all registered observer     */    public void unregisterAll() {        synchronized(mObservers) {            mObservers.clear();        }            }}

ConcreateSubject(具體目標),實現的方法同Oberver一樣,只不過它是通知ArrayList<Observer>下的每個Oberver去執行各自的action。

源碼路徑:framework/base/core/java/android/database/DataSetObservable.java

package android.database;/** * A specialization of Observable for DataSetObserver that provides methods for * invoking the various callback methods of DataSetObserver. */public class DataSetObservable extends Observable<DataSetObserver> {    /**     * Invokes onChanged on each observer. Called when the data set being observed has     * changed, and which when read contains the new state of the data.     */    public void notifyChanged() {        synchronized(mObservers) {            // since onChanged() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onChanged();            }        }    }    /**     * Invokes onInvalidated on each observer. Called when the data set being monitored     * has changed such that it is no longer valid.     */    public void notifyInvalidated() {        synchronized (mObservers) {            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onInvalidated();            }        }    }}

ConcreateObserver(具體觀察者),具體觀察者的任務是實實在在執行action的類,一般由開發人員根據實際情況,自己實現。android也有實現的例子

源碼路徑:

framework/base/core/java/android/widget/AbsListView.java

    class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {        @Override        public void onChanged() {            super.onChanged();            if (mFastScroller != null) {                mFastScroller.onSectionsChanged();            }        }        @Override        public void onInvalidated() {            super.onInvalidated();            if (mFastScroller != null) {                mFastScroller.onSectionsChanged();            }        }    }

framework/base/core/java/android/widget/AdapterView.java

    class AdapterDataSetObserver extends DataSetObserver {        private Parcelable mInstanceState = null;        @Override        public void onChanged() {            mDataChanged = true;            mOldItemCount = mItemCount;            mItemCount = getAdapter().getCount();            if (DBG) {                Xlog.d(TAG, "AdapterView onChanged: mOldItemCount = " + mOldItemCount                        + ",mItemCount = " + mItemCount + ",getAdapter() = " + getAdapter()                        + ",AdapterView = " + AdapterView.this, new Throwable("onChanged"));            }            // 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();        }        @Override        public void onInvalidated() {            mDataChanged = true;                        if (DBG) {                Xlog.d(TAG, "AdapterView onInvalidated: mOldItemCount = " + mOldItemCount                        + ",mItemCount = " + mItemCount + ",getAdapter() = " + getAdapter()                        + ",AdapterView = " + AdapterView.this, new Throwable("onInvalidated"));            }            if (AdapterView.this.getAdapter().hasStableIds()) {                // Remember the current state for the case where our hosting activity is being                // stopped and later restarted                mInstanceState = AdapterView.this.onSaveInstanceState();            }            // Data is invalid so we should reset our state            mOldItemCount = mItemCount;            mItemCount = 0;            mSelectedPosition = INVALID_POSITION;            mSelectedRowId = INVALID_ROW_ID;            mNextSelectedPosition = INVALID_POSITION;            mNextSelectedRowId = INVALID_ROW_ID;            mNeedSync = false;            checkFocus();            requestLayout();        }        public void clearSavedState() {            mInstanceState = null;        }    }

執行個體:

型運用是大家熟悉的BaseAdapter,BaseAdapter關聯了一個DataSetObservable對象,並實現registerDataSetObserver和unregisterDataSetObserver兩個方法實現註冊和撤銷Observer,方法notifyDataSetChanged間接調用Observer的實現者的onChange()方法,以達到通知數據改變的作用。使用ListView和BaseAdapter組合時,當BaseAdapter的item改變時,我們經常會調用notifyDataSetChanged(),通知Listview重新整理。

但是,但是,但是,我們從來沒有調用BaseAdapter的registerDataSetObserver(DataSetObserver observer)註冊Observer,那麼Listview如何接收到通知,並執行重新整理動作呢?

我們來看看ListView做了什麼

    /**     * Sets the data behind this ListView.     *     * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},     * depending on the ListView features currently in use. For instance, adding     * headers and/or footers will cause the adapter to be wrapped.     *     * @param adapter The ListAdapter which is responsible for maintaining the     *        data backing this list and for producing a view to represent an     *        item in that data set.     *     * @see #getAdapter()      */    @Override    public void setAdapter(ListAdapter adapter) {        if (mAdapter != null && mDataSetObserver != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);        }        resetList();        mRecycler.clear();        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();    }

注意下面3行

            mAdapter = adapter;
            mDataSetObserver = new AdapterDataSetObserver();            mAdapter.registerDataSetObserver(mDataSetObserver);

當我們setAdapter(ListAdapter adapter)時,BaseAdapter同時註冊了AdapterDataSetObserver(),至於AdapterDataSetObserver是如何通知Listvew和每個子item重新整理(invalidate)的,這裡涉及到的內容已經超出文章的範圍,具體請查看源碼。

其實,Android用到DataSetObserver的地方很多,Cursor,WebView,Adapter,...非常之多。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.