Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 設計模式 之 觀察者模式

Android 設計模式 之 觀察者模式

編輯:關於Android編程

[java] 
/*
 * 觀察者模式
 *      定義對象間的一種一個(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
[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
[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
[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
[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
[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做了什麼
[java] 
/**
 * 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行
[java] 
mAdapter = adapter; 
[java]  www.2cto.com
mDataSetObserver = new AdapterDataSetObserver(); 
mAdapter.registerDataSetObserver(mDataSetObserver); 
當我們setAdapter(ListAdapter adapter)時,BaseAdapter同時注冊了AdapterDataSetObserver(),至於AdapterDataSetObserver是如何通知Listvew和每個子item刷新(invalidate)的,這裡涉及到的內容已經超出文章的范圍,具體請查看源碼。

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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved