从ListView源码看观察者模式

       在使用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相关方法。


流程图如下:






致谢:
本文部分内容来自:
http://www.jianshu.com/p/ad4cc25a9cf5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值