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来实现。有时间的话可以去研究研究。
- android设计模式的使用之观察者模式
- Android中观察者设计模式的使用
- Android 设计模式 之 观察者模式
- Android 设计模式 之 观察者模式
- Android 设计模式之观察者模式
- Android 设计模式 之 观察者模式
- android 中的设计模式 之 观察者模式
- Android 设计模式 之 观察者模式
- android设计模式之观察者模式
- Android 设计模式之观察者模式
- Android 设计模式之观察者模式
- android设计模式之观察者模式详解
- android设计模式之--观察者模式
- Android 设计模式 之 观察者模式
- Android 设计模式 之 观察者模式详解
- Android设计模式之观察者模式
- Android设计模式之观察者模式
- Android设计模式学习之观察者模式
- 为openvpn创建tap虚拟网卡
- Android中视频录制常见问题
- LeetCode 第 191 题 (Number of 1 Bits)
- DrawerLayout 模仿google官方左滑,menu内容延伸到通知栏
- Linux下装无线网卡(Ubuntu)
- android设计模式的使用之观察者模式
- crontab--定时器命令
- ns3中路由协议仿真的shell脚本编写
- 实现一个函数,可以左旋字符串中K个字符
- #leetcode#217 Contains Duplicate (map&排序)
- Linux文件第二扩展文件系统属性
- MLeaksFinder:精准 iOS 内存泄露检测工具
- fork出的分支如何保持和原分支同步更新
- 武汉科技大学网络赛1578: LAMP