最熟悉的陌生人:ListView 中的观察者模式

来源:互联网 发布:3ds数据搬家 编辑:程序博客网 时间:2024/06/05 06:56

http://blog.csdn.net/u011240877/article/details/52683711

RecyclerView 得宠之前,ListView 可以说是我们用的最多的组件。之前一直没有好好看看它的源码,知其然不知其所以然。

今天我们来窥一窥 ListView 中的观察者模式。

不熟悉观察者模式的可以看看这篇 观察者模式 : 一支穿云箭,千军万马来相见 巩固一下。

在我们使用 ListView 的过程中,经常需要修改 Item 的状态,比如添加、删除、选中等等,通常的操作是在对数据源进行操作后,调用 notifyDataSetChanged() ,比如:

    public void addData(String data) {        if (mData != null) {            mData.add(data);            notifyDataSetChanged();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

随后 ListView 中的数据就会更新,我们可以猜到这个过程是把全部 Item View 重新绘制、数据绑定了一遍,这个场景跟观察者模式很一致,具体怎么实现的呢

前方高能预警,代码太多看不下去的可以先翻到篇尾看看流程图,有点印象再回来继续啃的,不然容易晕。

1.首先我们跟进去看下 notifyDataSetChanged() 源码,进入了系统的 BaseAdapter

    /**     * 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();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

看注释,“通知观察者数据已经改变,任何和数据集绑定的 View 都应该刷新”,的确是观察者模式。

那发布者、观察者是谁?在什么时候注册的?观察者的 notifyChanged() 方法又做了什么呢?

2.在 BaseAdapter 中我们可以看到这几个方法:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {    private final DataSetObservable mDataSetObservable = new DataSetObservable();    public boolean hasStableIds() {        return false;    }    /**    * BaseAdapter 提供了 注册订阅方法    */    public void registerDataSetObserver(DataSetObserver observer) {        mDataSetObservable.registerObserver(observer);    }    /**    * 还提供了 解除订阅方法    */    public void unregisterDataSetObserver(DataSetObserver observer) {        mDataSetObservable.unregisterObserver(observer);    }    /**     * 数据更新时通知观察者     */    public void notifyDataSetChanged() {        mDataSetObservable.notifyChanged();    }    /**     * 提醒观察者散了,别看了,数据不可用了     * /    public void notifyDataSetInvalidated() {        mDataSetObservable.notifyInvalidated();    }    //省略无关代码}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

BaseAdapter 提供了 注册订阅、解除订阅、提醒观察者数据更新、告诉观察者数据不可用 等关键方法。

其中 DataSetObservable 是发布者:

/** * A specialization of {@link Observable} for {@link DataSetObserver} * that provides methods for sending notifications to a list of * {@link DataSetObserver} objects. */public class DataSetObservable extends Observable<DataSetObserver> {    /**     * 发出更新提醒     */    public void notifyChanged() {        synchronized(mObservers) {            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onChanged();            }        }    }    /**     * 发出数据集无法使用通知     */    public void notifyInvalidated() {        synchronized (mObservers) {            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onInvalidated();            }        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

可以看到 notifyChanged 方法的注释中,是倒序遍历观察者集合并进行通知,这是为了避免观察者列表的 iterator 被使用时,进行删除操作导致出问题。

DataSetObservable 继承自 Observable < DataSetObserver > ,看下 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);        }    }    /**     * 移除所有观察者     */    public void unregisterAll() {        synchronized(mObservers) {            mObservers.clear();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

DataSetObserver 就是观察者抽象类,将来需要被具体观察者者继承:

/** * DataSetObserver must be implemented by objects which are added to a DataSetObservable. */public abstract class DataSetObserver {    /**     * 数据改变时调用     */    public void onChanged() {        // Do nothing    }    /**     * 数据不可用时调用     */    public void onInvalidated() {        // Do nothing    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

了解发布者、观察者基类后,接下来去看下在什么时候进行注册、通知。

3.ListView.setAdapter 源码:

public void setAdapter(ListAdapter adapter) {        //移除旧的观察者        if (mAdapter != null && mDataSetObserver != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);        }        //省略不相关内容...        if (mAdapter != null) {            //...            //初始化新观察者并注册            mDataSetObserver = new AdapterDataSetObserver();            mAdapter.registerDataSetObserver(mDataSetObserver);            //...            if (mItemCount == 0) {                // Nothing selected                checkSelectionChanged();            }        } else {            mAreAllItemsSelectable = true;            checkFocus();            // Nothing selected            checkSelectionChanged();        }        requestLayout();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

可以看到在 ListView.setAdapter 方法中,先解除旧的观察者,然后初始化了新的观察者 AdapterDataSetObserver 并注册。

而 AdapterDataSetObserver 定义在 ListView 的父类 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();            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

AdapterDataSetObserver 继承自 AdapterView.AdapterDataSetObserver,在 onChanged 和 onInvalidated 方法中先调用 AdapterView.AdapterDataSetObserver 对应的方法,然后调用了 mFastScroll.onSectionsChanged();

先看 AdapterView.AdapterDataSetObserver :

class AdapterDataSetObserver extends DataSetObserver {        private Parcelable mInstanceState = null;        @Override        public void onChanged() {            //更新 数据修改状态            mDataChanged = true;            //更新 数据数量            mOldItemCount = mItemCount;            //更新 ItemView 数量            mItemCount = getAdapter().getCount();            // 监测是否有数据之前不可用、现在可用            // 由于 BaseAdapter.hasStableIds() 默认返回 false ,所以我们直接看 else            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()) {                // 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;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

看 onChanged() 方法,这个方法中先后更新了 数据更新状态(mDataChanged ),数据数量,而由于 BaseAdapter.hasStableIds() 默认返回 false , 所以我们直接看 else 情况下 rememberSyncState 方法:

 /**     * 保存屏幕状态     *     */    void rememberSyncState() {        if (getChildCount() > 0) {            mNeedSync = true;            mSyncHeight = mLayoutHeight;            if (mSelectedPosition >= 0) {                //如果选择了内容,保存选择的位置和距离顶部的偏移量                View v = getChildAt(mSelectedPosition - mFirstPosition);                mSyncRowId = mNextSelectedRowId;                mSyncPosition = mNextSelectedPosition;                if (v != null) {                    mSpecificTop = v.getTop();                }                mSyncMode = SYNC_SELECTED_POSITION;            } else {                // 如果没有选择内容就保存第一个 View 的偏移量                View v = getChildAt(0);                T adapter = getAdapter();                if (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) {                    mSyncRowId = adapter.getItemId(mFirstPosition);                } else {                    mSyncRowId = NO_ID;                }                mSyncPosition = mFirstPosition;                if (v != null) {                    mSpecificTop = v.getTop();                }                mSyncMode = SYNC_FIRST_POSITION;            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

rememberSyncState 方法中针对是否选择了 item,保存了当前状态,重新绘制时会恢复状态。当我们滑动 ListView 后进行刷新数据操作,ListView 并没有滚动到顶部,就是因为这个方法的缘故。

回到 AdapterDataSetObserver.onChanged() 方法:

class AdapterDataSetObserver extends DataSetObserver {        @Override        public void onChanged() {            //更新 数据修改状态            mDataChanged = true;            //更新 数据数量            mOldItemCount = mItemCount;            //更新 ItemView 数量            mItemCount = getAdapter().getCount();            // 监测是否有数据之前不可用、现在可用            // 由于 BaseAdapter.hasStableIds() 默认返回 false ,所以我们直接看 else            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null                    && mOldItemCount == 0 && mItemCount > 0) {                AdapterView.this.onRestoreInstanceState(mInstanceState);                mInstanceState = null;            } else {               //记录当前状态,接下来刷新时要用到这些状态                rememberSyncState();            }            checkFocus();            requestLayout();        }        //...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

保存数据状态后,进入 chekFocus 方法:

    void checkFocus() {        final T adapter = getAdapter();        final boolean empty = adapter == null || adapter.getCount() == 0;        final boolean focusable = !empty || isInFilterMode();        // The order in which we set focusable in touch mode/focusable may matter        // for the client, see View.setFocusableInTouchMode() comments for more        // details        super.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState);        super.setFocusable(focusable && mDesiredFocusableState);        if (mEmptyView != null) {            updateEmptyStatus((adapter == null) || adapter.isEmpty());        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里设置 Focus 和 FocusableInTouchMode 状态,关于 FocusableInTouchMode 不熟悉的可以 查看这篇文章

最后终于到了 View 的重新绘制 requestLayout, 这里将遍历 View 树重新绘制:

public void requestLayout() {        if (mMeasureCache != null) mMeasureCache.clear();        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {            // Only trigger request-during-layout logic if this is the view requesting it,            // not the views in its parent hierarchy            ViewRootImpl viewRoot = getViewRootImpl();            if (viewRoot != null && viewRoot.isInLayout()) {                if (!viewRoot.requestLayoutDuringLayout(this)) {                    return;                }            }            mAttachInfo.mViewRequestingLayout = this;        }        mPrivateFlags |= PFLAG_FORCE_LAYOUT;        mPrivateFlags |= PFLAG_INVALIDATED;        if (mParent != null && !mParent.isLayoutRequested()) {            mParent.requestLayout();        }        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {            mAttachInfo.mViewRequestingLayout = null;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

至此,我们了解了 ListView 中的观察者模式的大概流程,看得人快吐血了,一层调一层啊,还是画个 UML 图和流程图来回顾一下:

ListView 中的观察者模式

这里写图片描述

ListView 注册观察者 流程图 :

这里写图片描述

ListView 通知观察者更新 流程图 :

这里写图片描述


0 0
原创粉丝点击