对于程序员来说,不过你属于哪个品种(c++,java,js,c#...)这些特定的语言都是在不断更迭的,熟悉的SDK也总有一天会被淘汰掉,所以在我看来,真正能属于自己的就是那些融入你的思维中的东西,例如设计模式。
这也是我下决心研究设计模式的最大动力。在此先推荐一本好书《设计模式解析》 by - Alan Shalloway, James R. Trott
本人也是尝试过无数次学习设计模式,然后无数次的半途而废,一来体验不到设计模式的甜头,再就是大多数书籍都是泛泛而谈感觉没什么收获,直到看到这本书,才真正得以坚持学了下来。
这次打算先学以致用一下。之前有想为什么Adapter中的数据可以更新到ListView中去呢?于是就去读AdapterView的源码,但是里面的各种Observer把我搞得有点晕,就放弃了。今天刚好学了Observer模式,就从这个角度来分析一下AdapterView的源码。如有错误,欢迎指出。
既然是从Observer模式的角度来分析,首先说明一下什么是Observer模式。由于主要还是分析源码,所以对于Observer模式的介绍不会太过详细,大家有需要的可以去网络上搜索其他资源来学习。
1.Observer模式介绍
Observer模式主要分为两个主体:Observer(观察者) 和 Subject(目标/被观察者)
两个主体之间的关系有以下约束:
a.所有观察者必须以同样的方式工作,即实现共同的接口
b.观察者必须要注册到Subject(被观察者)中,才能观察到Subject的变动
c.当Subject被观察的部分出现了变动,Subject要主动通知Observer(观察者)
d.Observer可以从Subject中获取到需要的信息
对于Android开发者来说,最熟悉的Observer模式就是Button.setOnClickListener(OnClickListener listener)来注册点击事件。
那么这里,我们简单分析一下点击事件的过程。
Observer(观察者)就是 实现了OnClickListener接口的Activity(或其他比如Fragment等)
Subject(被观察者)就是Button这个控件
当Button被点击之后,Button内部就会调用OnClickListener的onClick(View v)方法,以这种方式来通知Activity(观察者)——“我被点击了,有事赶紧办”
而Activity在实现OnClickListener接口时,就已经实现了onClick方法,于是当Button被点击时Activity该办的事情就可以正常的执行了。
当然,Observer模式要比我说的这个更复杂,因为篇幅和水平有限,大家还是去学一些更专业的资源来了解这个模式吧。
接下来我们来看AdapterView和Adapter之间是如何协同工作的。
2.AdapterView源码分析
首先,我们来确定观察者和被观察者。
AdapterView的子类有很多我们常用的控件,比如ListView,GridView等。
我们就拿ListView来做分析吧!
ListView和BaseAdapter之间,很显然ListView是观察者,而BaseAdapter是被观察者。当BaseAdapter中的数据有变化时,就通知ListView让它刷新显示的数据。
既然AdapterView是观察者,那么肯定会有一个公共的接口用来让BaseAdapter(被观察者)在数据变化时去通知AdapterView做一些相应的操作。
所以我打算先找出这个“公共的接口”,观察AdapterView中的源码发现,这个“公共接口”就是DataSetObserver定义如下
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
* {@link Cursor}.
*/
public void onInvalidated() {
// Do nothing
}
}
我们看到这并不是一个接口,而是一个抽象类(其实Java中的接口与抽象类功能都差不多)。
里面定义了两个方法onChanged()和onInvalidated(),很显然这两个方法就是被观察者用来通知观察者的桥梁。
另外我们注意到,严格的说AdapterView并不是观察者,真正的观察者是AdapterDataSetObserver,在AdapterView中定义了一个这个类型的属性,具体代码我们在后面分析。
好了,"公共接口"找到了,接下来看一下观察者如何注册到被观察者中。
首先看一下BaseAdapter的代码,我们发现跟AdapterView类似,BaseAdapter本身并不是被观察者,被观察者是它的一个属性:mDataSetObservable
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
//...省略大量代码...
}
我们看到在BaseAdapter中定义了注册和注销的公共方法,很明显这个是给观察者用来注册和注销用的。相当于View.setOnClickListener()方法的作用。
顺便我们也来看一下真正的被观察者的代码
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();
}
}
}
}
代码量很少,就是定义了两个方法,跟上面的DataSetObserver抽象类中onChanged()和onInvalidated()方法对应。
在方法中将那些注册近来的观察者遍历一遍,分别调用它们的onChanged()方法和onInvalidated()方法,这个步骤就是在通知观察者数据变化了。
到这里,其实整个流程就已经比较完整了。讲了如何注册,如何通知观察者,然后观察者被通知执行相应的代码就ok了。
如果你还是一脸蒙蔽,那我带你走一遍流程你就明白了。
实例分析
你用了一个ListView来显示一些数据,现在数据更新了你会怎么做?
大家都知道,肯定是调用Adapter.notifyDataSetChanged()方法。看源码
/**
* 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();
}
就是调用DataSetObservable类(被观察者)的notifyChanged()方法,通知AdapterView中的DataSetObserver对象(观察者)的onChanged()方法,来让AdapterView更新数据,在这里就是让ListView更新数据。
说了这么多,其实就是上面这一句话而已。
当我们知道了整个流程之后,就可以很清晰的知道我们真正要研究的代码在哪里了,没错,就是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();
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;
}
}
这个类是定义在AdapterView的内部类。大概就是判断一下各种状态,然后通知系统重新绘制AdapterView。
那么,整篇文章到这里就要结束了。其实内容并不复杂,主要是想分享一种研究源码的方法吧。
也对设计模式的推广出一份力,让大家能体会一下设计模式的好处。
如有疑问,欢迎讨论。