android设计模式的使用之观察者模式

来源:互联网 发布:电力工程计价软件 编辑:程序博客网 时间:2024/05/17 06:54

在分析android源码前,先来了解观察者模式的定义与使用。


一.简介
1.定义:对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
2.角色:观察者(Observer),被观察者(Observable)
     观察者:(Observer)将自己注册到被观察对象(Subject)中,被观察对象将观察者存放在一个容器(Container)里。
     被观察者:被观察对象发生了某种变化(如图中的SomeChange),从容器中得到所有注册过的观察者,将变化通知观察者。

3.根据对它们各自的定义,观察者/被观察者需要具备以下几个特性:
被观察者:
      (1).需要一个容器来保存所以的观察者,如下定义的ArrayList集合类。
      (2).需要一个注册到容器的方法,及从容器的释放的方法,如registerObserver和unregisterObserver。
      (3).当被观察者自身改变时,需要一个方法来通知所有的观察者,如notifyChanged

Public class Observable< Observer >{     ArrayList< Observer > mObservers = new ArrayList< Observer >();     public void registerObserver(Observer observer){}     public void unregisterObserver(Observer observer) {}     public void notifyChanged() {…. onChanged ()……}}
观察者:

      提供一个接口,当被观察者自身改变时,观察者需要做出什么动作。

Public class Observer{public void onChanged(){}}

以上是对于观察者于被观察者的定义。那么这种模式是怎么使用的呢?一般我们实现好观察者Observer后,需要用registerObserver把Observer注册到Observable中,这个Observer最后是保存在了Observable的集合中,当某个地方需要通知Observer改变时,就调用notifyChanged方法,这个方法会循环Observable的集合,并调用集合中的Observer对象的onChanged方法,达到了通知的目的。

二.源代码分析:
 源代码中很多地方都使用了观察者模式,其中最为常见的有以下几种:
被观察者:DataSetObservable,ContentObservable,Observable,ContentService
观察者:  DataSetObserver, ContentObserver,
其中,DataSetObservable与ContentObservable都是继承自Observable,它们对应的观察者是DataSetObserver与ContentObserver。ContentService从特性上来看,也属于被观察者之列,它对应的观察者是ContentObserver。ContentService的实现比较复杂,这个后面会说到。
围绕上面的几种被观察者-观察者模式,催生了Android中的数据消息通知机制,主要涉及的几个使用类如下:
BaseAdapter,AbstractCursor,CursorAdapter

现在,让我们先从基础类来看一下,然后再接下去分析以上的几个类。


(一).基础类Observable:

public abstract class Observable<T> {    protected final ArrayList<T> mObservers = new ArrayList<T>();    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);        }    }   public void unregisterObserver(T observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized(mObservers) {            int index = mObservers.indexOf(observer);            if (index == -1) {                throw new IllegalStateException("Observer " + observer + " was not registered.");            }            mObservers.remove(index);        }    }    public void unregisterAll() {        synchronized(mObservers) {            mObservers.clear();        }}}

基础类Observable主要定义了一个集合变量mObservers,一个注册的方法registerObserver,一个取消注册的方法unregisterObserver。它的通知方法notifyChanged主要由它的子类实现。DataSetObservable和ContentObservable都继承于Observable。

(二).DataSetObservable与DataSetObserver


被观察者:

public class DataSetObservable extends Observable<DataSetObserver> {    public void notifyChanged() {        synchronized(mObservers) {            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onChanged();            }        }    }    public void notifyInvalidated() {        synchronized (mObservers) {            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onInvalidated();            }        }    }}

观察者:

public abstract class DataSetObserver {    public void onChanged() {        // Do nothing    }    public void onInvalidated() {        // Do nothing    }}    private class MyDataSetObserver extends DataSetObserver {        @Override        public void onChanged() {            mDataValid = true;            notifyDataSetChanged();        }        @Override        public void onInvalidated() {            mDataValid = false;            notifyDataSetInvalidated();        }    }}

与其他的两种模式相比,DataSetObservable与DataSetObserver是最容易理解的一个模式。在大致了解了被观察者-观察者之间的关系后,我们可以研究一下比较深入的东西了。接下来研究一下我们最常用的一个例子:适配器BaseAdapter。在使用适配器的时候,我们知道,只要把相应的数据和容器适配上,每当数据改变时,我们所见到的页面都会自动的改变,那么这个自动改变到底是怎么做到的呢?答案就在于观察者模式。
    以下是一个ListActivity与Adapter的适配过程。ConversationList是短信列表页面,在ConversationList的OnCreate()方法中,调用了setListAdapter()方法进行适配。适配结束后,如果手动删除了某个短信会话,会执行onDraftChanged方法。如下图:

  



上图的流程可以分为两条:
1.    注册过程:在适配的时候把观察者AdapterDataSetObserver注册到DataSetObservable中,并调用requestLayout方法,最终会调用到view中的requestLayout方法,这个方法的作用就是重新绘制UI。BaseAdapter的部分代码如下:

        private final DataSetObservable mDataSetObservable = new DataSetObservable();        public void registerDataSetObserver(DataSetObserver observer) {             mDataSetObservable.registerObserver(observer);        }        public void notifyDataSetChanged() {            mDataSetObservable.notifyChanged();        }

2.通知过程:当有数据变化时,调用BaseAdapter的notifyDataSetChanged方法,通过DataSetObservable进行消息通知,最后会执行观察者的onChanged方法。最终达到重新绘制UI的目的。
以上就是有关于DataSetObservable与DataSetObserver的单独使用的例子。使用者BaseAdapter对外提供了相应的接口来实现这一功能。

(三).ContentObservable与ContentObserver
    
要讲这个模式之前,我们先来看一个ListView加载数据的例子,如下图:





上图是我们进入短信列表的时候,从数据库中加载的数据并显示的过程。如果对于LoaderManager加载数据不熟悉的同学,可以先去预习一下。从步骤5开始,ConversationList已经拿到了需要加载的Cursor类型的数据。接下来它会执行CursorAdapter的swapCursor()方法,并注册两个观察者。注册结束后,会调用notifyDataSetChanged()进行页面更新,这样我们所见到的短信列表就显示出来了。上面我们已经讲过,ConversationList会在OnCreate()方法中设置适配器setListAdapter(),在OnStart()方法中加载数据并更新UI。

接下来了解一下以下两个类:AbstractCursor,CursorAdapter
我们看看它们的继承关系:
   
               

其中AbstractCursor的部分代码如下:

    private final DataSetObservable mDataSetObservable = new DataSetObservable();    private final ContentObservable mContentObservable = new ContentObservable();    public void registerDataSetObserver(DataSetObserver observer) {        mDataSetObservable.registerObserver(observer);    }    public void registerContentObserver(ContentObserver observer) {        mContentObservable.registerObserver(observer);    }    public void setNotificationUri(ContentResolver cr, Uri notifyUri, int userHandle) {        synchronized (mSelfObserverLock) {            mNotifyUri = notifyUri;            mContentResolver = cr;            if (mSelfObserver != null) {                mContentResolver.unregisterContentObserver(mSelfObserver);            }            mSelfObserver = new SelfContentObserver(this);            mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver, userHandle);            mSelfObserverRegistered = true;        }}

从代码中,我们可以看到,在这个AbstractCursor中,这三个模式的观察者都使用到了。所以它有三种注册方法:
1.    registerContentObserver注册到ContentObservable中。
2.    registerDataSetObserver注册到DataSetObservable中。
3.    setNotificationUri,注意这个注册方法,在这里使用了ContentResolver的registerContentObserver方法,而ContentResolver正是使用了ContentService观察者模式。在接下来的分析中,知道这个是非常重要的。
CursorAdapter:从命名上来看,它兼顾了适配器Adapter与cursor数据的整合,实际上也是这样的。既然它继承了BaseAdapter,那么它也具有了根据适配的内容自动更新页面的功能。它的部分代码如下:
    public Cursor swapCursor(Cursor newCursor) {        if (newCursor == mCursor) {            return null;        }        Cursor oldCursor = mCursor;        if (oldCursor != null) {            if (mChangeObserver != null)                 oldCursor.unregisterContentObserver(mChangeObserver);            if (mDataSetObserver != null)                 oldCursor.unregisterDataSetObserver(mDataSetObserver);        }        mCursor = newCursor;        if (newCursor != null) {            if (mChangeObserver != null)                 newCursor.registerContentObserver(mChangeObserver);            if (mDataSetObserver != null)                 newCursor.registerDataSetObserver(mDataSetObserver);            mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");            mDataValid = true;            // notify the observers about the new cursor            notifyDataSetChanged();        } else {            mRowIDColumn = -1;            mDataValid = false;            // notify the observers about the lack of a data set            notifyDataSetInvalidated();        }        return oldCursor;    }

在这个方法中,它调用了AbstractCursor的registerContentObserver和registerDataSetObserver方法,并调用了父类的notifyDataSetChanged方法,是不是很熟悉,这个最终会走到View更新UI的过程。好了,铺垫得差不多了,也该放张图了:



上图可以分为两部分来看:
      注册过程:可以看到有三种注册方法,其中两种是注册到ContentService中,一种是注册到ContentObservable中。如果使用setNotificationUri进行注册,那么我们不用自己实现观察者类,它使用的将是SelfContentObserver。看看它需要传入的参数:
public void setNotificationUri(ContentResolver cr, Uri notifyUri)。
ContentResolver对象我们可以很方便的得到,需要在意的是Uri而已,这意味着我们如果需要监听某个uri,只需要传入正确的uri,并适配好,当数据改变时,UI就会自动更新。上图中的13对应的是7,意味着需要自己实现观察者类,并实现其onChange的逻辑即可。至于1中的registerContentObserver方法,它的使用是在CursorAdapter中。
      通知过程:从6开始,当插入数据时,其会调用ContentService通知SelfContentObserver执行onChange()方法。而这个onChange()方法也调用dispatchChange()对ContentObservable中的ChangeObserver进行通知,接下来就是普通的调用,直到调用notifyChanged()方法对DataSetObservable的MyDataSetObserver进行通知,最后就是更新UI的过程。可以看到,在这过程中,使用了三次被观察者-观察者的消息通知机制,也是比较混乱的,想要缕清其中的关系,就得抓住:什么时候注册,注册的是谁,什么时候通知,通知的是谁?那么,为什么使用了这么多的观察者模式?


补充:
ContentService的实现也是相当复杂的。主要复杂在于它管理观察者的方式上,ContentService使用了树形结构来保存注册上来的观察者,其节点类为ObserverNode,而且它的使用一般是通过ContentResolver来实现。有时间的话可以去研究研究。



 

0 0
原创粉丝点击