在使用ListView时,常常会使用会使用mAdapter.notifyDataSetChanged()来更新数据,达到更新ListView效果。notifyDataSetChanged()是如何达到更新数据的呢?其实底层原理就是使用观察者模式。什么是观察者模式?定义对象之前一种一对多的依赖关系,使得当一个对象改变状态,所有依赖这个对象的对象都会得到通知并且自动更新。
结构和UML图
- Subject:抽象主题,也就是被观察者(Observable)角色,抽象主题角色把所有的观察者的引用保存在一个集合中,每个主题都可以有任意数量的观察者,抽象主题提供一个接口么可以添加/删除观察者对象。
- ConcreteSubject:具体主题,又名具体被观察者,该觉得将有关组状态保存进具体的观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,观察者的抽象类,定义了一个更新接口,使得在得到主题的更改通知时来更改自己。
- ConcreteSubject:具体的观察者,实现了抽象观察者角色所定义的更新接口,以便在主题的状态改变时更新自身的状态
两个抽象类或者接口,
一个是被观察者的抽象类或者接口,需要持有所有观察者的引用,
被观察者要能做三件事儿:
1.可以注册观察者。
2.有注册就有注销。
3.可以通知观察者的方法。
观察者要能做一件事儿,那就是更新自己。一个是观察者的抽象类或者接口,用来更新自己。
简单示例
虽然我们主要是写android的,但是服务器的东西还是要懂点,就拿简书这个类似于博客的网站来说吧,如果你订阅了或者说关注了一个领域,就能收到这个领域文章的推送,如果没有关注,则不能。是相当于有一个总控制台(被观察者,持有数据源,这里的数据源是我们每个订阅了的人)通知下面的观察者。
在java中可以这么写。(因为java的观察者模式是内置的,所以你只需要实现Observer接口和继承Observable)
观察者的代码:
/**
* 程序员,也就是你,订阅这个专题的人是。观察者
*/
public class Coder implements Observer {
private String yourName;
public Coder(String yourName){
this.yourName=yourName;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("你订阅的"+arg.toString()+"更新了。");
}
@Override
public String toString() {
return "your name "+yourName;
}
}
以下是被观察者和服务器的代码。
/**
* 你订阅的简书android领域
*/
public class JianShuAndroid extends Observable{
public void postNewContentToCoder(String content){
setChanged();
notifyObservers(content);
}
}
/**
* 服务器的代码
*/
public class Server{
public static void main(String[] args){
JianShuAndroid jianShuAndroid=new JianShuAndroid();
Coder coder1=new Coder("name1");
Coder coder2=new Coder("name2");
Coder coder3=new Coder("name3");
jianShuAndroid.addObserver(coder1);
jianShuAndroid.addObserver(coder2);
jianShuAndroid.addObserver(coder3);
jianShuAndroid.postNewContentToCoder("contentChanged");
}
}
ListView源码中的观察者模式
在使用ListView时,我们需要为ListView设置Adapter即mListView.setAdapter();在ListView.setAdapter源码中有这样两行代码 mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
即我们在调用ListView.setAdapter的时候,源码中自动为我们注册了观察者。
第一行代码为我们定义了一个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();
}
其中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
}
...省略...
}
所以AdapterDataSetObserver和上文例子中的Coder类一样,是一个观察者。
再来看看registerDataSetObserver(),registerDataSetObserver是在BaseAdapter中实现的,
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);
}
其中定义了一个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();
}
}
}
发现DataSetObservable继承自Observable,上文给的例子JianShuAndroid也是继承了observable。所以DataSetObservable和JianShuAndroid是一样的。
还记得我们前面说被观察者一般要可以做三件事儿。注册,注销和通知。那么这里也不会例外。我们看到的是通知这件事儿,但是注册和注销是java内部的Observable帮我们实现了。
点击进入registerDataSetObserver();
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);
}
}
最后整理一遍这个过程,
在Adapter里面有一个DataSetObservable,是被观察者,被观察者必须有三个方法,注册,销毁,通知,这里的注册就是registerDataSetObserver,通知就是notify相关的。
在setAdapter的时候,将观察者,也就是AdapterDataObserver注册到DataSetObservable里面来维护,观察者里面自然是更新布局。
我们调用notifyDataSetChanged其实就是调用被观察者的notify相关方法。
在Adapter里面有一个DataSetObservable,是被观察者,被观察者必须有三个方法,注册,销毁,通知,这里的注册就是registerDataSetObserver,通知就是notify相关的。
在setAdapter的时候,将观察者,也就是AdapterDataObserver注册到DataSetObservable里面来维护,观察者里面自然是更新布局。
我们调用notifyDataSetChanged其实就是调用被观察者的notify相关方法。
流程图如下:
致谢:
本文部分内容来自:
http://www.jianshu.com/p/ad4cc25a9cf5