美文网首页Android开发经验谈Android开发程序员
ListView 数据与UI更新机制之观察者模式

ListView 数据与UI更新机制之观察者模式

作者: Tifkingsly | 来源:发表于2018-07-13 11:39 被阅读25次

    之前有一篇文章专门介绍观察者模式知识,当时通过EventBus来进行分析。近日在读《Android源码设计模式解析与实战》,看到书中介绍ListView中使用到观察者模式。为了加深对观察者模式的理解,以及掌握ListView的数据刷新机制,故分析其源码。

    ListView数据刷新简介:

    ListView使用中有一个非常重要的角色就是Adapter,其实这也是一个适配器模式的应用,外部需要增删改ListView的子元素时,常常通过Adapter的notifyDataSetChanged()方法来完成的,现在我们来看看其内部到底是如何实现。

    ListView数据刷新UML类图:

    image.png

    源码分析:

    EventBus是Android平台下用于应用内各组件或者模块间通信的一个类库,其主要实现思想为观察者模式。

    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);
        }
        
        /**
         * Notifies the attached observers that the underlying data has been changed
         * and any View reflecting the data set should refresh itself.
         */
        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;
        }
    }
    
    public class DataSetObservable extends Observable<DataSetObserver> {
        /**
         * Invokes {@link DataSetObserver#onChanged} on each observer.
         * Called when the contents of the data set have changed.  The recipient
         * will obtain the new contents the next time it queries the data set.
         */
        public void notifyChanged() {
            synchronized(mObservers) {
                //遍历通知所有观察者
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onChanged();
                }
            }
        }
    
        /**
         * Invokes {@link DataSetObserver#onInvalidated} on each observer.
         * Called when the data set is no longer valid and cannot be queried again,
         * such as when the data set has been closed.
         */
        public void notifyInvalidated() {
            synchronized (mObservers) {
                for (int i = mObservers.size() - 1; i >= 0; i--) {
                    mObservers.get(i).onInvalidated();
                }
            }
        }
    }
    
        //AbsListView中的内部类
        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();
                }
            }
        }
    
        //AdapterView中的内部类,上面AdapterDataSetObserver的父类
     class AdapterDataSetObserver extends DataSetObserver {
    
            private Parcelable mInstanceState = null;
    
            @Override
            public void onChanged() {
                mDataChanged = true;
                mOldItemCount = mItemCount;
                mItemCount = getAdapter().getCount();
    
                // 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();
                //请求刷新UI,实现数据变化时UI跟随变化
                requestLayout();
            }
    
            @Override
            public void onInvalidated() {
                mDataChanged = true;
    
                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;
            }
        }
    
    //ListView的setAdapter方法
     /**
         * 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();
                //创建一个数据集合观察者对象,并通过Adapter进行注册
                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();
        }
    

    看到上面的源码,在使用ListView时首先会为其设置一个Adapater对象,setAdapter执行时会创建一个数据集合观察者,并通过Adapter注册监听数据集合的变化。当数据集合发生变化时,用户端调用Adapater的notifyDataSetChanged方法,此时会调用DataSetObservable类的notifyChanged方法。而notifyChanged方法内部实现就是遍历其内部所持有的观察者集合,调用观察者的onChanged方法。真正实现数据变化而导致UI刷新的方法就在AdapterView中AdapterDataSetObserver的onChanged方法,该方法内部通过调用requestLayout请求重新布局,实现数据变化与UI变化的联动。其流程图如下:

    image.png

    结束语

    通过上述分析,相信大家对于观察者模式的印象会更深。源码比较简单,本文重点在于理解当ListView的数据集合发生变化时,如何引起UI随之变化的过程,至于底层实现没有设计,在于理解流程与设计模式的应用。

    相关文章

      网友评论

        本文标题:ListView 数据与UI更新机制之观察者模式

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