当前位置:   article > 正文

Android 中的设计模式之观察者模式_android 观察者模式

android 观察者模式

前言

观察者模式是我们开发工作中经常使用的开发模式。Android 源码中也有很多地方用到此模式。比如:ListView、ContentProvider 和 Broadcast 等等。本文将会介绍观察者模式、实现一个观察者模式并结合 Android 源码进行分析。

定义

定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。

介绍

观察者模式又被称作发布/订阅模式。即 1 人发布,N 人订阅。观察者模式主要用来解耦,将被观察者和观察者解耦,让他们之间者依赖关系很小,甚至不存在依赖。它最用的地方是 GUI 系统、订阅——发布系统。举个例子,我司开发的一个 App 左上角状态栏需要实时显示闹钟的状态,若存在设定好的闹钟则显示闹钟图标,否则不显示。因为存储闹钟使用的 ContentProvider,所以可以使用一个监听器监听闹钟数据是否发生变化,若发生变化就判断当前闹钟状态并显示或关闭图标。这样便实现了实时显示闹钟状态的功能。

UML 类图

手工实现

  • 创建观察者

实现抽象观察者 Observer 中的方法,这里创建一个 Coder 类,并定义收到通知后的动作:

  1. /**
  2. * 程序员是观察者
  3. *
  4. */publicclassCoderimplementsObserver{
  5. publicString name;
  6. publicCoder(String mName) {
  7. name = mName;
  8. }
  9. @Override
  10. publicvoidupdate(Observable arg0, Object arg1) {
  11. // TODO Auto-generated method stub
  12. System.out.println("嘿," + name + ",您的快递到了");
  13. }
  14. publicStringtoString(String name) {
  15. return"程序员:"+name;
  16. }
  17. }
  • 创建被观察者实现 Observable 方法,也就是快递员,快递到了会通知程序员们来拿快递:

  1. /**
  2. * Courier 即快递员,当他到公司时通知所有观察者(有快递的程序员)拿快递
  3. * @author Rickon
  4. *
  5. */
  6. public classCourierextendsObservable{
  7. public void postNewExpress(String content) {
  8. //标识状态或者内容发生改变(此处为快递到了)
  9. setChanged();
  10. //通知所有观察者
  11. notifyObservers(content);
  12. }
  13. }
  14. 复制代码
  • 测试

  1. publicclassTest {
  2. publicstaticvoidmain(String[] args) {
  3. //被观察者
  4. Couriercourier=newCourier();
  5. //观察者
  6. Codercoder1=newCoder("程序员张三");
  7. Codercoder2=newCoder("程序员李四");
  8. Codercoder3=newCoder("程序员王二麻子");
  9. //将观察者注册到被观察者的观察者列表中
  10. courier.addObserver(coder1);
  11. courier.addObserver(coder2);
  12. courier.addObserver(coder3);
  13. //快递到了
  14. courier.postNewExpress("快递到啦!");
  15. }
  16. }
  • 输出结果

  1. 嘿,程序员王二麻子,您的快递到了
  2. 嘿,程序员李四,您的快递到了
  3. 嘿,程序员张三,您的快递到了
  4. 复制代码

总结上述代码就实现了一个简单地观察者模式,使用的是 JDK 内部内置的 Observable(抽象被观察者),Observer(抽象观察者)。JDK 内置观察者模式也说明了观察者模式应用的广泛与重要性。

应用场景

  • 事件多级触发场景;

  • 当一个对象必须通知别的对象,而它又不能假定对象是谁;

  • 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优缺点

优点

  • 观察者与被观察者之间是抽象耦合,可以很好地应对业务变化;

  • 增强系统灵活性、可扩展性。

缺点

  • 开发过程中可能会出现一个被观察者、多个观察者的情况,开发和调试会变得比较复杂。除此之外,观察者太多的话收到通知需要消耗更长的时间。

Android 中的观察者模式

  • ListView 深入解析
    ListView 是 Android 中很常用的控件,我们在使用 ListView 添加数据后会调用 Adapter 的 notifyDataSetChanged()方法,这是为啥呢?下面我们来一探究竟!

我们发现 notifyDataSetChanged,这个方法是在 BaseAdapter 中定义的,具体带么如下:

  1. publicabstractclassBaseAdapterimplementsListAdapter, SpinnerAdapter {
  2. private final DataSetObservable mDataSetObservable = newDataSetObservable();
  3. publicbooleanhasStableIds() {
  4. returnfalse;
  5. }
  6. publicvoidregisterDataSetObserver(DataSetObserver observer) {
  7. mDataSetObservable.registerObserver(observer);
  8. }
  9. publicvoidunregisterDataSetObserver(DataSetObserver observer) {
  10. mDataSetObservable.unregisterObserver(observer);
  11. }
  12. /**
  13. * Notifies the attached observers that the underlying data has been changed
  14. * and any View reflecting the data set should refresh itself.
  15. */publicvoidnotifyDataSetChanged() {
  16. mDataSetObservable.notifyChanged();
  17. }
  18. //省略部分代码
  19. }

BaseAdapter 看起来是观察者模式,那么到底 BaseAdapter 是怎么运行的呢?又是如何实现的观察者模式,让我们继续分析。

首先看看 mDataSetObservable.notifyChanged() 方法:

  1. public classDataSetObservableextendsObservable<DataSetObserver>{
  2. /**
  3. * Invokes {@link DataSetObserver#onChanged} on each observer.
  4. * Called when the contents of the data set have changed. The recipient
  5. * will obtain the new contents the next time it queries the data set.
  6. */
  7. public void notifyChanged() {
  8. synchronized(mObservers) {
  9. // 遍历调用每个观察者的 onChanged() 方法来通知被观察者发生变化for (int i = mObservers.size() - 1; i >= 0; i--) {
  10. mObservers.get(i).onChanged();
  11. }
  12. }
  13. }
  14. //省略部分代码
  15. }

这里很容易看到此方法是遍历所有观察者,并且调用所有观察者的 onChnged() 方法,从而告知观察者发生了变化。那么这些观察者是怎么设置的呢?其实这些观察者就是 ListVIew 通过 setAdapter 方法产生的,我们看看这部分代码:

  1. @Override
  2. public void setAdapter(ListAdapter adapter) {
  3. //如果已经有了 Adapter,那么先注销 Adapter 对应的观察者
  4. if (mAdapter != null && mDataSetObserver != null) {
  5. mAdapter.unregisterDataSetObserver(mDataSetObserver);
  6. }
  7. //省略部分代码
  8. super.setAdapter(adapter);
  9. if (mAdapter != null) {
  10. mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
  11. mOldItemCount = mItemCount;
  12. //获取数据的数量
  13. mItemCount = mAdapter.getCount();
  14. checkFocus();
  15. //此处创建一个个数据集观察者
  16. mDataSetObserver = new AdapterDataSetObserver();
  17. //将观察者注册到 Adapter 中,实际上是注册到 DatasetObservable
  18. mAdapter.registerDataSetObserver(mDataSetObserver);
  19. //省略部分代码
  20. } else {
  21. //省略部分代码
  22. }
  23. requestLayout();
  24. }

从上述代码中可以看出,在设置 Adapter 时会构建一个 AdapterDataSetObserver,也就是观察者,之后再将这个观察者注册到 Adapter 中,这样我们的被观察者和观察者就都构建好了。那么 AdapterDataSetObserver 到底是怎么运行的呢?它是定义在 ListView 的父类 AbsListView 中,代码如下:

  1. classAdapterDataSetObserverextendsAdapterView<ListAdapter>.AdapterDataSetObserver{
  2. @Override
  3. public void onChanged() {
  4. super.onChanged();
  5. if (mFastScroll != null) {
  6. mFastScroll.onSectionsChanged();
  7. }
  8. }
  9. @Override
  10. public void onInvalidated() {
  11. super.onInvalidated();
  12. if (mFastScroll != null) {
  13. mFastScroll.onSectionsChanged();
  14. }
  15. }
  16. }

它又继承自 AbsListView 的父类 AdapterView 的 AdapterDataSetObserver,代码如下

  1. classAdapterDataSetObserverextendsDataSetObserver{
  2. privateParcelable mInstanceState = null;
  3. //前面提到的调用 Adapter 的 notifyDataSetChanged 时会调用所有观察者的 onChanged 方法,核心实现代码就在这里@Override
  4. public void onChanged() {
  5. mDataChanged = true;
  6. mOldItemCount = mItemCount;
  7. //获取 Adapter 中数据的数量
  8. mItemCount = getAdapter().getCount();
  9. // Detect the case where a cursor that was previously invalidated has// been repopulated with new data.if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
  10. && mOldItemCount == 0 && mItemCount > 0) {
  11. AdapterView.this.onRestoreInstanceState(mInstanceState);
  12. mInstanceState = null;
  13. } else {
  14. rememberSyncState();
  15. }
  16. checkFocus();
  17. //刷新 ListView、GridView 等 AdapterView 等组件
  18. requestLayout();
  19. }
  20. //省略部分代码
  21. }

源码分析到这里就很明显了,当我们调用 Adapter 的 notifyDataSetChanged 方法,这个方法又会调用 DataSetObservable 的 notifyDataSetChanged 方法,而这个方法会调用所有观察者的 onChanged 方法,在 onChanged 方法中则是会调用 ListView 的重新布局方法刷新UI,这就是一个完整的观察者模式。

ListView 观察者模式实现总结: 我重新概述这一过程:Adapter 中包含一个数据集可观察者 DataSetObservable,在数据数量发生变更时,开发者手动调用 Adapter.notifyDataSetChanged,实际上调用的则是 DataSetObservable 的 onChanged 方法,该方法会遍历所有观察者的 onChanged 方法。在 AdapterDataSetObserver 的 onChanged 函数中会获取 Adapter 中数据集的新数量,然后调用 ListView 的 requestLayout() 方法重新布局并更新 UI。

  • ContentProvider文章的开始有提到使用 ContentProvider 的过程中也用到了观察者模式。首先创建一个观察者对象,代码如下:

  1. ContentObserver alarmObserver = newContentObserver(newHandler()) {
  2. //创建观察者对象@OverridepublicvoidonChange(boolean selfChange) {
  3. super.onChange(selfChange);
  4. //执行业务代码
  5. }
  6. @OverridepublicvoidonChange(boolean selfChange, Uri uri) {
  7. super.onChange(selfChange, uri);
  8. }
  9. };

注册观察者:

mContext.getContentResolver().registerContentObserver(url, true, alarmObserver);

看到此注册方法有三个参数分别代表什么意义呢:

  • uri:针对对有变化的感兴趣进行监听的URI;

  • notifyForDescendents:true表示以uri前缀开始的任何变化都进行通知;false表示完全匹配才进行通知;

  • observer:IContentObserver接口,提供了一个方法onChange,变化发生时Cursor需要更新时调用

这样就通过指定Uri可以仅对数据库中感兴趣的数据有变化时,进行监听。具体源码就不再进行分析,大家感兴趣可以自行去分析源码,观察者模式是万变不离其宗的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/286287
推荐阅读
相关标签
  

闽ICP备14008679号