• home > OS > Android > Develop >

    Android设计模式之观察者模式中的DataSetObservable和DataSetObserver

    Author:zhoulujun@live.cn Date:

    Observable是观察者模式的典型应用。在Android下,Observable是一个泛型的抽象类,表示一个观察者对象,提供了观察者注册、反注册及清空三个方法,其源码如下:
    /* 
     * 观察者模式 
     *      定义对象间的一种一个(Subject)对多(Observer)的依赖关系,当一个对象的状态发送改变时,所以依赖于它的 
     * 对象都得到通知并被自动更新 
     *  
     * 当然,MVC只是Observer模式的一个实例。Observer模式要解决的问题为: 
     * 建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候, 
     * 依赖这个“一”的多也能够同步改变。最常见的一个例子就是:对同一组数据进行统计分析时候, 
     * 我们希望能够提供多种形式的表示(例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。 
     * 这些表示都依赖于同一组数据,我们当然需要当数据改变的时候,所有的统计的显示都能够同时改变。 
     * Observer模式就是解决了这一个问题。 
     *  
     * 适用性: 
     *      1. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面 
     *      将这两者封装成独立的对象中以使它们可以各自独立的改变和服用 
     *  
     *      2. 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变 
     *  
     *      3. 当一个对象必须通知其它对象,而它又不能假定其它对象是谁 
     *  
     * 参与者: 
     *      1. Subject(目标) 
     *      目标知道它的观察者,可以有任意多个观察者观察同一个目标 
     *      提供注册和删除观察者对象的接口 
     *  
     *      2. Observer(观察者) 
     *      为那些在目标发生改变时需获得通知的对象定义个更新的接口 
     *  
     *      3. ConcreteSubject(具体目标) 
     *      将有关状态存入各ConcreteObserver对象 
     *      当它的状态发送改变时,向它的各个观察者发出通知 
     *  
     *      4. ConcreteObserver(具体观察者) 
     *      维护一个指向ConcreteObserver对象的引用 
     *      存储有关状态,这些状态应与目标的状态保持一致 
     *      实现Observer的更新接口是自身状态与目标的状态保持一致 
     *       
     *  
     * */  
     public abstract class Observable<T> {  
      
        protected final ArrayList<T> mObservers = new ArrayList<T>();  
      
        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);  
            }  
        }  
      
        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);  
            }  
        }  
      
        public void unregisterAll() {  
            synchronized(mObservers) {  
                mObservers.clear();  
            }          
        }  
    }

     Observable的直接继承者有两个:DataSetObservable和ContentObservable。ContentObservable实现比较复杂,不过功能与DataSetObservable类似,这里只讲解DataSetObservable。

    观察者(DataSetObserver),目标(Observable<T>),具体目标(DataSetObserverable)

    DataSetObservable在很多的Adapter中都用到,像BaseAdapter。DataSetObservable使用DataSetObserver实例化了Observable。DataSetObserver表示了一个数据集对象的观察者,主要提供了两个方法:

    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,...非常之多。



    转载本站文章《Android设计模式之观察者模式中的DataSetObservable和DataSetObserver》,
    请注明出处:https://www.zhoulujun.cn/html/OS/Android/AndroidDevelop/2016_0415_7765.html