ListView 数据与UI更新机制之观察者模式
之前有一篇文章专门介绍观察者模式知识,当时通过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随之变化的过程,至于底层实现没有设计,在于理解流程与设计模式的应用。