观察者模式是一种使用频率非常高的模式,有时也被称作发布/订阅模式,属于行为型模式,它最常用的是 GUI 系统、订阅——发布系统,它一个重要作用就是解耦,使得它们之间的依赖性更小。观察者模式定义了对象间一种一对多的依赖关系,使得每当一个对象改变状态时,则所有依赖于它的对象都会得到通知并被自动更新。

1.观察者模式的使用情景

关联行为场景;事件多级触发场景;跨系统的消息交换场景(如消息队列、事件总线的处理机制)。

2.程序中使用观察者模式的优缺点

- 观察者模式
优点 观察者和被观察者之间是耦合抽象,应对业务变化;增强了系统灵活性、可扩展性。
缺点 开发调试变的比较复杂,Java中消息的通知是顺序执行,一个消息的卡顿会影响整体的执行效率,所以使用观察者模式还需要结合异步操作的方式。

3.观察者模式的UML类图

Subject:抽象主题,被观察(Observable)的角色;ConcreteSubject:具体主题;Observer:抽象观察者;ConcreteObserver:具体的观察者。

4.观察者模式的实现

观察者 Observer 和被观察者 Observable 是 JDK 中的内置类型。

1.创建观察者:

public class MyObserver implements Observer {
private String name;
public MyObserver(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + ", update:" + arg);
}
}

3.编写测试方法:

@Test
public void test() throws Exception {
// 被观察者
MyObservable observable = new MyObservable();
// 观察者
MyObserver observer1 = new MyObserver("test1");
MyObserver observer2 = new MyObserver("test2");
MyObserver observer3 = new MyObserver("test3");
MyObserver observer4 = new MyObserver("test4");
// 将观察者注册到被观察者对象的观察者列表中
observable.addObserver(observer1);
observable.addObserver(observer2);
observable.addObserver(observer3);
observable.addObserver(observer4);
// 发布消息
observable.postNewPublication("new");
}

输出结果:

test4, update:new
test3, update:new
test2, update:new
test1, update:new

可以看到所有订阅了被观察者的观察者都接收到了更新消息,一对多的订阅——发布系统就完成了。

5.Android系统源代码中的应用情景

1.notifyDataSetChanged() 方法

我们在使用 ListView 添加数据后,都会调用 Adapter 的 notifyDataSetChanged() 方法来动态更新数据。

notifyDataSetChanged() 方法被定义在 BaseAdapter 中,BaseAdapter 就是一个观察者模式:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
// 数据集观察者
private final DataSetObservable mDataSetObservable = new DataSetObservable();
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> {
// 调用每个观察者的 onChanged() 方法来通知它们被观察者发生了变化
public void notifyChanged() {
synchronized(mObservers) {
// 调用所有观察者的 onChanged() 方法
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
// 代码省略
}

这段代码就是在 mDataSetObservable.notifyChanged() 中遍历所有观察者,并且调用它们的 onChanged() 方法,从而告知观察者发生了变化。

那么这些观察者是哪里来的呢?其实是 ListView 通过 setAdapter() 方法设置 Adapter 产生的,我们来看看相关代码:

public class ListView extends AbsListView {
// 代码省略
@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 中。

到这里,我们就知道了,当 ListView 的数据发生变化时,调用 Adapter 的 notifyDataSetChanged() 方法,这个方法又调用 DataSetObservable 的 notifyChanged() 方法,这个方法又调用所有观察者(AdapterDataSetObserver)的 onChanged() 方法,在 onChanged() 方法中又会调用 ListView 重新布局的方法使得 ListView 刷新界面,这就是一个观察者模式。

Android 设计模式情景分析——观察者模式的更多相关文章

  1. 《Android系统源代码情景分析》连载回忆录:灵感之源

    上个月,在花了一年半时间之后,写了55篇文章,分析完成了Chromium在Android上的实现,以及Android基于Chromium实现的WebView.学到了很多东西,不过也挺累的,平均不到两个 ...

  2. Android系统--Binder系统具体框架分析(二)Binder驱动情景分析

    Android系统--Binder系统具体框架分析(二)Binder驱动情景分析 1. Binder驱动情景分析 1.1 进程间通信三要素 源 目的:handle表示"服务",即向 ...

  3. Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件

    Android系统--输入系统(十三)Dispatcher线程情景分析_Reader线程传递事件 1. 输入按键 我们知道Android系统的按键分为三类:(1)Global Key;(2)Syste ...

  4. Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理

    Android系统--输入系统(十四)Dispatcher线程情景分析_dispatch前处理 1. 回顾 我们知道Android输入系统是Reader线程通过驱动程序得到上报的输入事件,还要经过处理 ...

  5. Android : 跟我学Binder --- (4) 驱动情景分析

    目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...

  6. 10.7 android输入系统_Dispatcher线程情景分析_Reader线程传递事件和dispatch前处理

    android输入系统C++最上层文件是com_android_serve_input_InputManagerService.cpp global key:按下按键,启动某个APP可以自己指定,修改 ...

  7. Android 设计模式 之 观察者模式

    /* * 观察者模式 *      定义对象间的一种一个(Subject)对多(Observer)的依赖关系,当一个对象的状态发送改变时,所以依赖于它的 * 对象都得到通知并被自动更新 * * 当然, ...

  8. Android 设计模式之观察者模式(转载自:“http://blog.csdn.net/fangchongbory/article/details/7774044”)

    /* * 观察者模式 *      定义对象间的一种一个(Subject)对多(Observer)的依赖关系,当一个对象的状态发送改变时,所以依赖于它的 * 对象都得到通知并被自动更新 * * 当然, ...

  9. Android设计模式系列

    http://www.cnblogs.com/qianxudetianxia/category/312863.html Android设计模式系列(12)--SDK源码之生成器模式(建造者模式) 摘要 ...

随机推荐

  1. 暑假集训第一周比赛G题

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=83146#problem/G G - 向 Crawling in process... C ...

  2. STL中流相关的输入输出符和get函数彻底总结:cin、cin.get()、cin.getline()、getline()、gets()等函数的用法

    我的总结: [首先:对于流来说,就把流看成一个中转的仓库,对流进行<<运算或>>运算或者get函数的运算都是指,把“流”中的数据“运送”到“内存变量”中去,还是把内存变量中的数 ...

  3. CentOS下查看MySQL的安装路径

    Linux下查看mysql.apache是否安装,并卸载. 指令 ps -ef|grep mysql 得出结果 root     17659     1  0  2011 ?        00:00 ...

  4. linux学习系列三

    1. 账户与账户安全 账户和组是操作系统的基本概念,linux的组有基本组和附加组之分,一个用户只可以加入到一个基本组中国,但是可以加入到多个附加组中.创建用户时,系统默认会自动创建同名的组,并设置用 ...

  5. Spark- 优化后的 shuffle 操作原理剖析

    在spark新版本中,引入了 consolidation 机制,也就是说提出了ShuffleGroup的概念.一个 ShuffleMapTask 将数据写入 ResultTask 数量的本地文本,这个 ...

  6. WCF寄宿(Host)之自我寄宿(Self-Hosting)简单实例【Console应用为宿主】

    前言: 由于最近的项目 中需要用到WCF,所以又回头翻了翻,阅读了大量园中大神的博文,故而做个总结. 谬误之处,万望不吝指教! 闲话不叙! 一.寄宿(Host)WCF服务  1)一种是为一组WCF服务 ...

  7. 封装一个简单的Hibernate SessionFactory

    封装Hibernate框架中的session工厂   ,方便很多,免去了很多重复无用的代码 package com.maya.test; import org.hibernate.*; import ...

  8. ffmpeg代码实现自定义encoder

    1.概述 本文主要讲述如何用ffmpeg代码实现自己的encoder. 2.代码 /* *本程序主要实现一个自己的encoder并加入到encoder链中去,供api调用 *作者:缪国凯(MK) *8 ...

  9. Windows PCM音频捕获与播放实现

    在WINDOWS下,音频函数有多种类型,如MCI.多媒体OLE控制.高级音频等,使用方法都比较简单.但如果想编写一个功能较强大的音频处理程序,那就必须使用低级音频函数和多媒体文件I/O来控制音频设备的 ...

  10. bzoj 1023 [SHOI2008]cactus仙人掌图 ( poj 3567 Cactus Reloaded )——仙人掌直径模板

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1023 http://poj.org/problem?id=3567 因为lyd在讲课,所以有 ...