美文网首页
Android中ListView的订阅模式

Android中ListView的订阅模式

作者: yangzai | 来源:发表于2016-04-28 13:45 被阅读189次

    我们每次在使用ListView的时候,如果数据改变了,都会调用adapter的notifyDataSetChanged方法,那么我们来看看为什么调用该方法就可以刷新数据了。
    首选我们要了解订阅模式,因为notifyDataSetChanged就是使用该模式。先看下UML建模图吧


    我们先介绍下各个类:

    • Observer:抽象观察者
    • ConcreteObserverA:观察者对象实体
    • Observable:抽象被观察者
    • ConcreteObservers:被观察者实体

    这是一个最简单的观察者模式,目标对象能够添加和删除观察者,当自己某种状态或者行为发生改变时,可通过notifyObservers通知注册的观察者进行更新操作。

    了解了订阅模式,那我们看看ListView与Adapter是怎么实现的。先从我们最常调用的notifyDataSetChanged方法切入吧

        public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
        private final DataSetObservable mDataSetObservable = new DataSetObservable();
        
            public void registerDataSetObserver(DataSetObserver observer) {
                mDataSetObservable.registerObserver(observer);
            }
        
            public void unregisterDataSetObserver(DataSetObserver observer) {
                mDataSetObservable.unregisterObserver(observer);
            }
    
            public void notifyDataSetChanged() {
                mDataSetObservable.notifyChanged();
            }
    
        }
    

    BaseAdapter中有一个成员变量mDataSetObservable,我们的注册,注销都是调用它的方法,那么我们看看它是什么

        public class DataSetObservable extends Observable<DataSetObserver> {
          
            public void notifyChanged() {
                synchronized(mObservers) {
                    for (int i = mObservers.size() - 1; i >= 0; i--) {
                        mObservers.get(i).onChanged();
                    }
                }
            }
        }
    
    

    这里只有一个简单的方法notifyChanged,重要的东西应该在它的父类。DataSetObservable是继承Observable的,那我们看看Observable

        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);
                }
            }
        }
    

    这时候我们就能看到被观察者的影子了,有一个集合,来存储观察者的引用,等到数据变化时再遍历通知,只是没有notify方法,因为它的子类提供了该方法。
    所以DataSetObservable就是我们的被观察者,它可以添加、删除、通知观察者。被观察者我们找到了,那么观察者有是谁呢?刚才有没有注意到BaseAdapter
    中有两个方法,将一个DataSetObserver从我们的被观察者DataSetObservable中注册、注销。那么DataSetObservable肯定是我们要找的
    观察者了,看看谁调用了这两个方法;我们看看listView的SetAdapter,这里调用了adapter的注册与注销方法(把观察者绑定到被观察者上)

        public void setAdapter(ListAdapter adapter) {
            if (mAdapter != null && mDataSetObserver != null) {
                mAdapter.unregisterDataSetObserver(mDataSetObserver);//注销
            }
                ...
            if (mAdapter != null) {
                ...
                mDataSetObserver = new AdapterDataSetObserver();
                mAdapter.registerDataSetObserver(mDataSetObserver);//重新注册
                ...
            } else {
                ...
            }
    
            requestLayout();
        }
    
    

    ListView先将之前注册的DataSetObserver注销掉,然后再new一个新的AdapterDataSetObserver注册到我们的被观察者上
    这样我们就找到了观察者AdapterDataSetObserver,其实AdapterDataSetObserver是AbsListView中的内部类

        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();
                }
            }
        }
    
    

    从AdapterDataSetObserver中我们没有找到更新UI的迹象,那么我们看看它的父类

         class AdapterDataSetObserver extends DataSetObserver {
    
            private Parcelable mInstanceState = null;
    
            @Override
            public void onChanged() {
                mDataChanged = true;
                mOldItemCount = mItemCount;
                mItemCount = getAdapter().getCount();
    
                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 (AdapterView.this.getAdapter().hasStableIds()) {
                    mInstanceState = AdapterView.this.onSaveInstanceState();
                }
    
                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;
            }
        }
    
    

    这里我们看到在listview中调用了requestLayout(),应该就是这里更新的。先保留悬念,我们继续理清类的关系

    AdapterDataSetObserver最终是继承自DataSetObserver

        
        public abstract class DataSetObserver {
            public void onChanged() {
                // Do nothing
            }
        
            public void onInvalidated() {
                // Do nothing
            }
        }
    
    

    DataSetObserver跟我们UML图中的Observer很像?到这里我们就找到了订阅模式中所有的元素,让我来列举下

    • DataSetObserver:抽象观察者
    • AdapterDataSetObserver:观察者实体
    • Observable:抽象被观察者
    • DataSetObservable:被观察者实体

    既然找到了订阅模式中这些元素,那么我们分析下adapter与listView是如何使用订阅模式的;
    当adapter中的数据发生改变时,adapter会调用notifyDataSetChanged方法,这个方法会调用被观察者DataSetObservable
    的notifyChanged方法,这个方法又会遍历调用所有观察者AdapterDataSetObserver的onChanged方法。在AdapterDataSetObserver
    父类onChanged方法中会重新计算adapter的数据个数,并调用ListView的requestLayout方法重新布局,更新用户界面。
    至此就解释了为什么adapter.notifyDataSetChanged方法会更新界面

    ListView订阅模式UML(关系指向不准,仅供参考)


    相关文章

      网友评论

          本文标题:Android中ListView的订阅模式

          本文链接:https://www.haomeiwen.com/subject/akkhrttx.html