Android编程设计模式之观察者模式实例详解

 更新时间:2017年12月22日 08:58:46   作者:蹲街式等待  
这篇文章主要介绍了Android编程设计模式之观察者模式,结合实例形式详细分析了Android观察者模式的概念、功能、实现方法及相关使用注意事项,需要的朋友可以参考下

本文实例讲述了Android编程设计模式之观察者模式。分享给大家供大家参考,具体如下:

一、介绍

观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统、订阅——发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也会经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者模式此时就派上用场了。

二、定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

三、使用场景

关联行为场景,需要注意的是,关联行为是可拆分的,而不是”组合“关系。

事件多级触发场景。

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

四、观察者模式的UML类图

UML类图:

角色介绍:

Subject:抽象主题,也就是被观察者(Observable)的角色,抽象主题角色把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫做具体被观察者(ConcreteObservable)角色。

Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。

ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便主题的状态发生改变化时更新自身的状态。

五、简单实现

这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们,下来就简单实现一下。

抽象观察者类:

/**
 * 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
 */
public interface Observer {
  /**
   * 有更新
   * 
   * @param message 消息
   */
  public void update(String message);
}

抽象被观察者类:

/**
 * 抽象被观察者类
 */
public interface Observable {
  /**
   * 推送消息
   * 
   * @param message 内容
   */
  void push(String message);
  /**
   * 订阅
   * 
   * @param observer 订阅者
   */
  void register(Observer observer);
}

具体的观察者类:

/**
 * 具体的观察者类,也就是订阅者
 */
public class User implements Observer {
  @Override
  public void update(String message) {
    System.out.println(name + "," + message + "更新了!");
  }
  // 订阅者的名字
  private String name;
  public User(String name) {
    this.name = name;
  }
}

具体的被观察者类:

/**
 * 具体的被观察者类,也就是订阅的节目
 */
public class Teleplay implements Observable{
  private List<Observer> list = new ArrayList<Observer>();//储存订阅者
  @Override
  public void push(String message) {
    for(Observer observer:list){
      observer.update(message);
    }
  }
  @Override
  public void register(Observer observer) {
    list.add(observer);
  }
}

实现:

public class Client {
  public static void main(String[] args) {
    //被观察者,这里就是用户订阅的电视剧
    Teleplay teleplay = new Teleplay();
    //观察者,这里就是订阅用户
    User user1 = new User("小明");
    User user2 = new User("小光");
    User user3 = new User("小兰");
    //订阅
    teleplay.register(user1);
    teleplay.register(user2);
    teleplay.register(user3);
    //推送新消息
    teleplay.push("xxx电视剧");
  }
}

结果:

小明,xxx电视剧更新了!
小光,xxx电视剧更新了!
小兰,xxx电视剧更新了!

由上面的代码可以看出实现了一对多的消息推送,推送消息都是依赖Observer和Observable这些抽象类,而User和Teleplay完全没有耦合,保证了订阅系统的灵活性和可扩展性。

六、Android源码中的观察者模式

1、BaseAdapter

BaseAdapter我相信大家都不陌生,在ListView的适配器中我们都是继承它。下面来简单分析分析。

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);
  }
  /**
   * 当数据集变化时,通知所有观察者
   */
  public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
  }
}

看看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) {
      // since onChanged() is implemented by the app, it could do anything, including
      // removing itself from {@link mObservers} - and that could cause problems if
      // an iterator is used on the ArrayList {@link mObservers}.
      // to avoid such problems, just march thru the list in the reverse order.
      for (int i = mObservers.size() - 1; i >= 0; i--) {
        mObservers.get(i).onChanged();
      }
    }
  }
}

可以看出在mDataSetObservable.notifyChanged()中遍历所有观察者,并调用他们的onChanged(),从而告知观察者发生了什么。

那么观察者怎么来的,那就是setAdapter方法,代码如下:

@Override
public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
      mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    resetList();
    mRecycler.clear();
    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
      mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
      mAdapter = adapter;
    }
    mOldSelectedPosition = INVALID_POSITION;
    mOldSelectedRowId = INVALID_ROW_ID;
    // AbsListView#setAdapter will update choice mode states.
    super.setAdapter(adapter);
    if (mAdapter != null) {
      mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
      mOldItemCount = mItemCount;
      mItemCount = mAdapter.getCount();
      checkFocus();
      mDataSetObserver = new AdapterDataSetObserver();
      mAdapter.registerDataSetObserver(mDataSetObserver);//注册观察者
      ......省略
    }
}

AdapterDataSetObserver定义在ListView的父类AbsListView中,是一个数据集观察者,代码:

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
  @Override
  public void onChanged() {
    super.onChanged();
    if (mFastScroller != null) {
      mFastScroller.onSectionsChanged();
    }
  }
  @Override
  public void onInvalidated() {
    super.onInvalidated();
    if (mFastScroller != null) {
      mFastScroller.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();
  }
  // 代码省略
  public void clearSavedState() {
    mInstanceState = null;
  }
}

当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservablenotifyChanged函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的onChanged方法。这就是一个观察者模式!

七、总结

优点:

观察者和被观察者之间是抽象耦合,应对业务变化。

增强系统的灵活性和可扩展性。

缺点:

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结

希望本文所述对大家Android程序设计有所帮助。

相关文章

  • Android MaterialAlertDialogBuilder修改按钮属性

    Android MaterialAlertDialogBuilder修改按钮属性

    这篇文章主要介绍了Android MaterialAlertDialogBuilder修改按钮属性实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Android中通过RxJava进行响应式程序设计的入门指南

    Android中通过RxJava进行响应式程序设计的入门指南

    响应式编程在Android中的运用是非常犀利的,比如在异常处理和调度器方面,这里我们将从生命周期等方面来讲解Android中通过RxJava进行响应式程序设计的入门指南:
    2016-06-06
  • Android动画之雷达扫描效果

    Android动画之雷达扫描效果

    雷达扫描效果在我们日常会经常看到,比如在新浪微博上有一个雷达功能,感觉类似于微信附近的人。只是多了一个类似于雷达扫描效果的动画,某些知名安全软件也有这样的雷达效果,因此今天在这里小编带着大家学习一下。
    2016-08-08
  • Android AlertDialog的几种用法详解

    Android AlertDialog的几种用法详解

    这篇文章主要介绍了Android AlertDialog的几种用法详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Android NDK开发入门

    Android NDK开发入门

    本文主要对NDK产生的背景、使用NDK原因、NDK简介、NDK开发环境的搭建、如何运行NDK提供的事例demo等进行了详细的介绍。具有很好的参考价值,需要的朋友一起来看下吧
    2016-12-12
  • Android时分秒计时器的两种实现方法

    Android时分秒计时器的两种实现方法

    这篇文章主要介绍了Android时分秒计时器的两种实现方法,分别是Chronometer控件和handler+timer+timerTask方式,非常不错,感兴趣的朋友一起看下吧
    2016-08-08
  • OpenGL Shader实现物件材料效果详解

    OpenGL Shader实现物件材料效果详解

    在一些主流app上有一些比较特殊的滤镜效果,例如灰尘、塑料封面、光影效果等,这些其实是纹理叠加的效果。本文将用OpenGL Shader实现这些效果,需要的可以参考一下
    2022-02-02
  • android判断设备是否有相机的实例代码

    android判断设备是否有相机的实例代码

    下面小编就为大家带来一篇android判断设备是否有相机的实例代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • ReactiveCocoa代码实践之-RAC网络请求重构

    ReactiveCocoa代码实践之-RAC网络请求重构

    这篇文章主要介绍了ReactiveCocoa代码实践之-RAC网络请求重构 的相关资料,需要的朋友可以参考下
    2016-04-04
  • Android  两个Fragment之间的跳转和数据的传递实例详解

    Android 两个Fragment之间的跳转和数据的传递实例详解

    这篇文章主要介绍了Android 两个Fragment之间的跳转和数据的传递实例详解的相关资料,这里说明实现的思路及实现方法,需要的朋友可以参考下
    2017-07-07

最新评论