赞
踩
观察者模式是我们开发工作中经常使用的开发模式。Android 源码中也有很多地方用到此模式。比如:ListView、ContentProvider 和 Broadcast 等等。本文将会介绍观察者模式、实现一个观察者模式并结合 Android 源码进行分析。
定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。
观察者模式又被称作发布/订阅模式。即 1 人发布,N 人订阅。观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间者依赖关系很小,甚至不存在依赖。它最用的地方是 GUI 系统、订阅——发布系统。举个例子,我司开发的一个 App 左上角状态栏需要实时显示闹钟的状态,若存在设定好的闹钟则显示闹钟图标,否则不显示。因为存储闹钟使用的 ContentProvider,所以可以使用一个监听器监听闹钟数据是否发生变化,若发生变化就判断当前闹钟状态并显示或关闭图标。这样便实现了实时显示闹钟状态的功能。
创建观察者
实现抽象观察者 Observer 中的方法,这里创建一个 Coder 类,并定义收到通知后的动作:
- /**
- * 程序员是观察者
- *
- */publicclassCoderimplementsObserver{
- publicString name;
-
- publicCoder(String mName) {
- name = mName;
- }
-
- @Override
- publicvoidupdate(Observable arg0, Object arg1) {
- // TODO Auto-generated method stub
- System.out.println("嘿," + name + ",您的快递到了");
- }
-
- publicStringtoString(String name) {
- return"程序员:"+name;
- }
- }

创建被观察者实现 Observable 方法,也就是快递员,快递到了会通知程序员们来拿快递:
- /**
- * Courier 即快递员,当他到公司时通知所有观察者(有快递的程序员)拿快递
- * @author Rickon
- *
- */
- public classCourierextendsObservable{
- public void postNewExpress(String content) {
- //标识状态或者内容发生改变(此处为快递到了)
- setChanged();
-
- //通知所有观察者
- notifyObservers(content);
- }
- }
- 复制代码
测试
- publicclassTest {
-
- publicstaticvoidmain(String[] args) {
- //被观察者
- Couriercourier=newCourier();
- //观察者
- Codercoder1=newCoder("程序员张三");
- Codercoder2=newCoder("程序员李四");
- Codercoder3=newCoder("程序员王二麻子");
-
- //将观察者注册到被观察者的观察者列表中
- courier.addObserver(coder1);
- courier.addObserver(coder2);
- courier.addObserver(coder3);
-
- //快递到了
- courier.postNewExpress("快递到啦!");
- }
-
- }

输出结果
- 嘿,程序员王二麻子,您的快递到了
- 嘿,程序员李四,您的快递到了
- 嘿,程序员张三,您的快递到了
- 复制代码
总结上述代码就实现了一个简单地观察者模式,使用的是 JDK 内部内置的 Observable(抽象被观察者),Observer(抽象观察者)。JDK 内置观察者模式也说明了观察者模式应用的广泛与重要性。
事件多级触发场景;
当一个对象必须通知别的对象,而它又不能假定对象是谁;
跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优点
观察者与被观察者之间是抽象耦合,可以很好地应对业务变化;
增强系统灵活性、可扩展性。
缺点
开发过程中可能会出现一个被观察者、多个观察者的情况,开发和调试会变得比较复杂。除此之外,观察者太多的话收到通知需要消耗更长的时间。
ListView 深入解析
ListView 是 Android 中很常用的控件,我们在使用 ListView 添加数据后会调用 Adapter 的 notifyDataSetChanged()方法,这是为啥呢?下面我们来一探究竟!
我们发现 notifyDataSetChanged,这个方法是在 BaseAdapter 中定义的,具体带么如下:
- publicabstractclassBaseAdapterimplementsListAdapter, SpinnerAdapter {
- private final DataSetObservable mDataSetObservable = newDataSetObservable();
-
- publicbooleanhasStableIds() {
- returnfalse;
- }
-
- publicvoidregisterDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.registerObserver(observer);
- }
-
- publicvoidunregisterDataSetObserver(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.
- */publicvoidnotifyDataSetChanged() {
- mDataSetObservable.notifyChanged();
- }
-
- //省略部分代码
- }

BaseAdapter 看起来是观察者模式,那么到底 BaseAdapter 是怎么运行的呢?又是如何实现的观察者模式,让我们继续分析。
首先看看 mDataSetObservable.notifyChanged() 方法:
- public classDataSetObservableextendsObservable<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 中,代码如下:
- classAdapterDataSetObserverextendsAdapterView<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,代码如下
- classAdapterDataSetObserverextendsDataSetObserver{
-
- privateParcelable 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 = newContentObserver(newHandler()) {
- //创建观察者对象@OverridepublicvoidonChange(boolean selfChange) {
- super.onChange(selfChange);
- //执行业务代码
- }
-
- @OverridepublicvoidonChange(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可以仅对数据库中感兴趣的数据有变化时,进行监听。具体源码就不再进行分析,大家感兴趣可以自行去分析源码,观察者模式是万变不离其宗的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。