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

在分析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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值