ListView中的观察者模式
Posted yinhuanxu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ListView中的观察者模式相关的知识,希望对你有一定的参考价值。
*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
虽然现在RecyclerView很好用,也在逐渐替代ListView。很多github的开源大神也在对其进行更加实用的封装。我现在写的一个音乐播放器也在使用RecyclerView。但是这些都不阻碍我们学习ListView优秀的源码设计。
播放器我想要写的精美,但现在越写越多bug,这也应该是我离开大学校园,实习前的最后一个小作品了,接下来的半年多的时间要冲刺复习咯。
进入正题,我用的是Api-23的源码。接下来就从源码的角度带你学习ListView中的观察者模式
当我们开启异步线程,向服务端拉取数据后,数据源已经更新了,此时想要更新ListView的视图以显示新的数据。
ListView使用了Adapter模式,很简单只需一行代码就能完成ListView的更新。
mAdapter.notifyDataSetChanged();
那么这里引出一个问题,
更新ListView的工作,是Adapter完成的还是ListView自身内部完成的?可以先猜想一下再往下看。
因为我之前已经学习过自定义控件,所以我看源码之前猜想是ListView完成的。惯性使然,我想到他可能是调用了onLayout(),onDraw()等方法呀,去重新布局,绘制
那接下来就解开疑惑吧。
先找到源头,从ListView绑定Adapter那里开始。
mListView.setAdapter(mAdapter);
ListView和Adapter就是用这行代码建立起关联的。
那么跟踪setAdapter方法进去:
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();
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();
}
方法是这样开始的
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
先判断mAdapter != null && mDataSetObserver != null
mAdapter肯定是不为null的,那么mDataSetObserver呢?这个引用是哪里被赋值的,先不管,继续往下看setAdapter方法。
这里先分享我看源码的方法吧:
刚开始的时候我是很喜欢往深处闯,导致看了一天都无法自拔,思路又散了。现在我看源码都是挑重点看,比如这个setAdapter方法,一路看下来都没有return 语句跳出,那么就一定会来到if(mAdapter !=null )这个判断,如下:
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
//代码省略
}
到了这里,我们也就找到了mDataSetObserver,原来是在这里被赋值的。
现在得出小结论:
1.在ListView的setAdapter方法中,生成了一个AdapterDataSetObserver对象并赋值给mDataSetObserver
2.调用Adapter的registerDataSetObserver方法将mDataSetObserver注册进去。
现在我们好奇的是Adapter的registerDataSetObserver方法。继续前进。
在BaseAdapter类中找到了registerDataSetObserver方法,并且也找到了经常调用的,很熟悉的notifyDataSetChanged方法。如下:
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();
}
//代码省略
}
可以看到,在registerDataSetObserver方法中,又调用了DataSetObservable的registerObserver方法将传进来的AdapterDataSetObserver对象注册进去,那么这个DataSetObservable又是什么呢?继续跟进
这个DataSetObservable源码比较少,那就全部贴出
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) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
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();
}
}
}
}
好像看不太懂。mObservers是什么?竟然没有registerObserver方法。哈哈,那肯定是父类继承下来的啊。在DataSetObservable类中暂时没我们想要知道的信息,那么就看看他的父类Observable吧。Observable还是个泛型。不管,看内部实现原理就好
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* Adds an observer to the list. The observer cannot be null and it must not already
* be registered.
* @param observer the observer to register
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is already registered
*/
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);
}
}
//代码省略
}
找到了registerObserver方法。代码逻辑还挺简单的。
我们又可以得出小结论:
DataSetObservable的内部维护着一个观察者集合,即源码中的mObservers。当我们的ListView绑定了Adapter,调用BaseAdapter的registerDataSetObserver方法时,实际上是在这个观察者集合mObservers里将该观察者添加进来。对ListView来说,这个观察者就是AdapterDataSetObserver
完成注册。以上就是setAdapter方法的源码分析
再看到BaseAdapter的notifyDataSetChanged()方法
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
内部调用了DataSetObservable的notifyChanged方法
再回到DataSetObservable的源码,看到notifyChanged()方法
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
从观察者集合里遍历出观察者,并调用该观察者的onChange()方法
很清楚了吧。
当我们调用Adapter的notifyDataSetChanged方法更新ListView。
在notifyDataSetChanged方法中又会调用DataSetObservable的notifyChanged方法。
而从DataSetObservable的源码中,我们知道了在notifyChanged方法中又会遍历出
AdapterDataSetObserver(观察者),并调用这个观察者的onChanged()方法。
完毕,底层实现就是这样。
接下来只需要知道AdapterDataSetObserver(观察者)的onChanged()方法里做了什么就好了。
而AdapterDataSetObserver,是ListView的父类AdapterView的一个内部类。他是真的有onChanged方法的。不信你看
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();
requestLayout();
}
//代码省略
}
终于揭开谜底,在AdapterDataSetObserver的onChanged()方法里,实际上是调用了View的requestLayout()方法进行重新策略,布局,绘制整个ListView的子项item view
requestLayout()的源码如下:
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;
}
}
AdapterView是继承ViewGroup的,但是ViewGroup并没有重写requestLayout()方法。有能力的同学可以继续深入研究AdapterView到底是怎么重新布局的
至此,我们已经解开了开篇的疑惑
综上所述,AdapterDataSetObserver这个是观察者,在AdapterDataSetObserver的onChanged函数中,实际上调用的是View中的方法完成了整个更新ListView的工作,AdapterDataSetObserver只是在外层进行了包装,真正的核心功能是ListView,更加准确的说话是ListView的父类AdapterView。
ListView就是通过Adapter模式,观察者模式,子项复用机制实现了视图良好的扩展性,节约了内存开销,提高了运行效率
以上是关于ListView中的观察者模式的主要内容,如果未能解决你的问题,请参考以下文章