内容简介:观察者模式是我们开发工作中经常使用的开发模式。Android 源码中也有很多地方用到此模式。比如:ListView、ContentProvider 和 Broadcast 等等。本文将会介绍观察者模式、实现一个观察者模式并结合 Android 源码进行分析。定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。观察者模式又被称作发布/订阅模式。即 1 人发布,N 人订阅。观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间者依赖关系很小,甚至不存在依
前言
观察者模式是我们开发工作中经常使用的开发模式。Android 源码中也有很多地方用到此模式。比如:ListView、ContentProvider 和 Broadcast 等等。本文将会介绍观察者模式、实现一个观察者模式并结合 Android 源码进行分析。
定义
定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。
介绍
观察者模式又被称作发布/订阅模式。即 1 人发布,N 人订阅。观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间者依赖关系很小,甚至不存在依赖。它最用的地方是 GUI 系统、订阅——发布系统。举个例子,我司开发的一个 App 左上角状态栏需要实时显示闹钟的状态,若存在设定好的闹钟则显示闹钟图标,否则不显示。因为存储闹钟使用的 ContentProvider,所以可以使用一个监听器监听闹钟数据是否发生变化,若发生变化就判断当前闹钟状态并显示或关闭图标。这样便实现了实时显示闹钟状态的功能。
UML 类图
手工实现
- 创建观察者
实现抽象观察者 Observer 中的方法,这里创建一个 Coder 类,并定义收到通知后的动作:
/** * 程序员是观察者 * */ public class Coder implements Observer{ public String name; public Coder(String mName) { name = mName; } @Override public void update(Observable arg0, Object arg1) { // TODO Auto-generated method stub System.out.println("嘿," + name + ",您的快递到了"); } public String toString(String name) { return "程序员:"+name; } } 复制代码
- 创建被观察者 实现 Observable 方法,也就是快递员,快递到了会通知 程序员 们来拿快递:
/** * Courier 即快递员,当他到公司时通知所有观察者(有快递的程序员)拿快递 * @author Rickon * */ public class Courier extends Observable { public void postNewExpress(String content) { //标识状态或者内容发生改变(此处为快递到了) setChanged(); //通知所有观察者 notifyObservers(content); } } 复制代码
- 测试
public class Test { public static void main(String[] args) { //被观察者 Courier courier = new Courier(); //观察者 Coder coder1 = new Coder("程序员张三"); Coder coder2 = new Coder("程序员李四"); Coder coder3 = new Coder("程序员王二麻子"); //将观察者注册到被观察者的观察者列表中 courier.addObserver(coder1); courier.addObserver(coder2); courier.addObserver(coder3); //快递到了 courier.postNewExpress("快递到啦!"); } } 复制代码
- 输出结果
嘿,程序员王二麻子,您的快递到了 嘿,程序员李四,您的快递到了 嘿,程序员张三,您的快递到了 复制代码
总结上述代码就实现了一个简单地观察者模式,使用的是 JDK 内部内置的 Observable(抽象被观察者),Observer(抽象观察者)。JDK 内置观察者模式也说明了观察者模式应用的广泛与重要性。
应用场景
- 事件多级触发场景;
- 当一个对象必须通知别的对象,而它又不能假定对象是谁;
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优缺点
优点
- 观察者与被观察者之间是抽象耦合,可以很好地应对业务变化;
- 增强系统灵活性、可扩展性。
缺点
- 开发过程中可能会出现一个被观察者、多个观察者的情况,开发和调试会变得比较复杂。除此之外,观察者太多的话收到通知需要消耗更长的时间。
Android 中的观察者模式
-
ListView 深入解析
ListView 是 Android 中很常用的控件,我们在使用 ListView 添加数据后会调用 Adapter 的 notifyDataSetChanged()方法,这是为啥呢?下面我们来一探究竟!
我们发现 notifyDataSetChanged,这个方法是在 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); } public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } /** * Notifies the attached observers that the underlying data has been changed * and any View reflecting the data set should refresh itself. */ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } //省略部分代码 } 复制代码
BaseAdapter 看起来是观察者模式,那么到底 BaseAdapter 是怎么运行的呢?又是如何实现的观察者模式,让我们继续分析。
首先看看 mDataSetObservable.notifyChanged() 方法:
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) { // 遍历调用每个观察者的 onChanged() 方法来通知被观察者发生变化 for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } //省略部分代码 } 复制代码
这里很容易看到此方法是遍历所有观察者,并且调用所有观察者的 onChnged() 方法,从而告知观察者发生了变化。那么这些观察者是怎么设置的呢?其实这些观察者就是 ListVIew 通过 setAdapter 方法产生的,我们看看这部分代码:
@Override public void setAdapter(ListAdapter adapter) { //如果已经有了 Adapter,那么先注销 Adapter 对应的观察者 if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } //省略部分代码 super.setAdapter(adapter); if (mAdapter != null) { mAreAllItemsSelectable = mAdapter.areAllItemsEnabled(); mOldItemCount = mItemCount; //获取数据的数量 mItemCount = mAdapter.getCount(); checkFocus(); //此处创建一个个数据集观察者 mDataSetObserver = new AdapterDataSetObserver(); //将观察者注册到 Adapter 中,实际上是注册到 DatasetObservable mAdapter.registerDataSetObserver(mDataSetObserver); //省略部分代码 } else { //省略部分代码 } requestLayout(); } 复制代码
从上述代码中可以看出,在设置 Adapter 时会构建一个 AdapterDataSetObserver,也就是观察者,之后再将这个观察者注册到 Adapter 中,这样我们的被观察者和观察者就都构建好了。那么 AdapterDataSetObserver 到底是怎么运行的呢?它是定义在 ListView 的父类 AbsListView 中,代码如下:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } } @Override public void onInvalidated() { super.onInvalidated(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } } } 复制代码
它又继承自 AbsListView 的父类 AdapterView 的 AdapterDataSetObserver,代码如下
class AdapterDataSetObserver extends DataSetObserver { private Parcelable mInstanceState = null; //前面提到的调用 Adapter 的 notifyDataSetChanged 时会调用所有观察者的 onChanged 方法,核心实现代码就在这里 @Override public void onChanged() { mDataChanged = true; mOldItemCount = mItemCount; //获取 Adapter 中数据的数量 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(); //刷新 ListView、GridView 等 AdapterView 等组件 requestLayout(); } //省略部分代码 } 复制代码
源码分析到这里就很明显了,当我们调用 Adapter 的 notifyDataSetChanged 方法,这个方法又会调用 DataSetObservable 的 notifyDataSetChanged 方法,而这个方法会调用所有观察者的 onChanged 方法,在 onChanged 方法中则是会调用 ListView 的重新布局方法刷新UI,这就是一个完整的观察者模式。
ListView 观察者模式实现总结:我重新概述这一过程:Adapter 中包含一个数据集可观察者 DataSetObservable,在数据数量发生变更时,开发者手动调用 Adapter.notifyDataSetChanged,实际上调用的则是 DataSetObservable 的 onChanged 方法,该方法会遍历所有观察者的 onChanged 方法。在 AdapterDataSetObserver 的 onChanged 函数中会获取 Adapter 中数据集的新数量,然后调用 ListView 的 requestLayout() 方法重新布局并更新 UI。
- ContentProvider 文章的开始有提到使用 ContentProvider 的过程中也用到了观察者模式。首先创建一个观察者对象,代码如下:
ContentObserver alarmObserver = new ContentObserver(new Handler()) { //创建观察者对象 @Override public void onChange(boolean selfChange) { super.onChange(selfChange); //执行业务代码 } @Override public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange, uri); } }; 复制代码
注册观察者:
mContext.getContentResolver().registerContentObserver(url, true, alarmObserver); 复制代码
看到此注册方法有三个参数分别代表什么意义呢:
- uri:针对对有变化的感兴趣进行监听的URI;
- notifyForDescendents:true表示以uri前缀开始的任何变化都进行通知;false表示完全匹配才进行通知;
- observer:IContentObserver接口,提供了一个方法onChange,变化发生时Cursor需要更新时调用
这样就通过指定Uri可以仅对数据库中感兴趣的数据有变化时,进行监听。具体源码就不再进行分析,大家感兴趣可以自行去分析源码,观察者模式是万变不离其宗的。
以上所述就是小编给大家介绍的《Android 中的设计模式:观察者模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。