第十四篇: 最熟悉的陌生人:ListView 中的观察者模式
2017-09-01 10:17
260 查看
RecyclerView 得宠之前,ListView 可以说是我们用的最多的组件。之前一直没有好好看看它的源码,知其然不知其所以然。
今天我们来窥一窥 ListView 中的观察者模式。
不熟悉观察者模式的可以看看这篇 观察者模式 : 一支穿云箭,千军万马来相见 巩固一下。
在我们使用 ListView 的过程中,经常需要修改 Item 的状态,比如添加、删除、选中等等,通常的操作是在对数据源进行操作后,调用 notifyDataSetChanged() ,比如:
2
3
4
5
6
1
2
3
4
5
6
随后 ListView 中的数据就会更新,我们可以猜到这个过程是把全部 Item View 重新绘制、数据绑定了一遍,这个场景跟观察者模式很一致,具体怎么实现的呢?
2
3
4
5
6
7
1
2
3
4
5
6
7
看注释,“通知观察者数据已经改变,任何和数据集绑定的 View 都应该刷新”,的确是观察者模式。
那发布者、观察者是谁?在什么时候注册的?观察者的 notifyChanged() 方法又做了什么呢?
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 是发布者:
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 源码:
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 就是观察者抽象类,将来需要被具体观察者者继承:
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
了解发布者、观察者基类后,接下来去看下在什么时候进行注册、通知。
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 中:
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();
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 方法:
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 并没有滚动到顶部,就是因为这个方法的缘故。
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 方法:
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 不熟悉的可以 查看这篇文章。
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 另外牛的一点就是可以加载各种各样的 Item View,这得益于当初设计的 Adapter,下篇文章我们来分析下 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 通知观察者更新 流程图 :
备注:
设计模式代码在这里ListView 另外牛的一点就是可以加载各种各样的 Item View,这得益于当初设计的 Adapter,下篇文章我们来分析下 ListView 中的适配器模式。
相关文章推荐
- 最熟悉的陌生人:ListView 中的观察者模式
- 最熟悉的陌生人:ListView 中的观察者模式
- 最熟悉的陌生人:ListView 中的观察者模式
- ListView中的观察者模式
- Android ListView与BaseAdapter的观察者模式实现
- Android ListView观察者模式浅析
- android 中ListView Adapter内所蕴含的观察者模式
- 从Android ListView 看Observer 观察者设计模式setOnClickListener
- ListView的观察者模式的应用,以及调用notifyDataSetChanged()方法时,为什么会刷新ListView
- 【进阶android】ListView源码分析——适配器及观察者模式
- ListView中的adapter的观察者模式
- ListView中的观察者模式
- ExpandableListView组件中CheckBox全选的使用(使用观察者模式)
- Android进阶系列--源码分析观察者模式在ListView的运用
- (转)设计模式之观察者模式在ListView中的应用
- 最常用的Java设计之一 观察者模式 在android中的实例 ListView Adapter机制
- ListView中的观察者模式
- Android设计模式源码解析之ListView观察者模式
- ListView观察者模式的应用
- 观察者设计模式--listView的源码分析