前言

观察者模式在Android开发中使用频率非常高,最常用的地方如订阅–发布系统,类似微信公众号用户订阅和接收消息的场景,因为这个模式最重要的功能就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至没有依赖。本文会继续将理论与实践结合,深入设计模式的总结。

文章目录

  • 前言
  • 观察者模式定义
  • 观察者模式使用场景
  • 观察者模式UML类图
  • 观察者模式简单实现
  • Android源码分析
    • 1.RecycleView中使用的观察者模式
    • 2.ListView中的使用
    • 3.BroadcastReceiver中的使用
    • 4.EventBus中的使用
    • 5.RxJava
    • 6.其他
  • 观察者模式的优点与缺点
    • 优点
    • 缺点
  • 在项目中的实际运用(实战)
  • 总结

观察者模式定义

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

观察者模式使用场景

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

  • 事件多级触发场景;

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

    具体场景如下:

1.有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息;

2.订阅杂志报刊,一旦订阅成功后,当有杂志报刊更新后,就会把杂志报刊送到你预留的地址

观察者模式UML类图


如上图所示,该模式包含四个角色:

  • 抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
  • 抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • 具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
  • 具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

观察者模式简单实现

下面以微信公众号订阅场景为例用代码具体说明。

1、定义一个抽象被观察者接口


/**** 抽象被观察者接口* 声明了添加、删除、通知观察者方法*/
public interface Observerable {public void registerObserver(Observer o);public void removeObserver(Observer o);public void notifyObserver();
}

2、定义一个抽象观察者接口

/**** 抽象观察者* 定义了一个update()方法,当被观察者调用notifyObservers()方法时,观察者的update()方法会被回调。*/
public interface Observer {public void update(String message);
}

3、定义被观察者,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。

/*** 被观察者,也就是微信公众号服务* 实现了Observerable接口,对Observerable接口的三个方法进行了具体实现**/
public class WechatServer implements Observerable {//注意到这个List集合的泛型参数为Observer接口,设计原则:面向接口编程而不是面向实现编程private List<Observer> list;private String message;public WechatServer() {list = new ArrayList<Observer>();}@Overridepublic void registerObserver(Observer o) {list.add(o);}@Overridepublic void removeObserver(Observer o) {if(!list.isEmpty())list.remove(o);}//遍历@Overridepublic void notifyObserver() {for(int i = 0; i < list.size(); i++) {Observer oserver = list.get(i);oserver.update(message);}}public void setInfomation(String s) {this.message = s;System.out.println("微信服务更新消息: " + s);//消息更新,通知所有观察者notifyObserver();}}

4、定义具体观察者,微信公众号的具体观察者为用户User

/*** 观察者* 实现了update方法**/
public class User implements Observer {private String name;private String message;public User(String name) {this.name = name;}@Overridepublic void update(String message) {this.message = message;read();}public void read() {System.out.println(name + " 收到推送消息: " + message);}}

5、编写一个测试类

首先注册了三个用户,ZhangSan、LiSi、WangWu。公众号发布了一条消息"PHP是世界上最好用的语言!",三个用户都收到了消息。

用户ZhangSan看到消息后颇为震惊,果断取消订阅,这时公众号又推送了一条消息,此时用户ZhangSan已经收不到消息,其他用户

还是正常能收到推送消息。

public class Test {public static void main(String[] args) {WechatServer server = new WechatServer();Observer userZhang = new User("ZhangSan");Observer userLi = new User("LiSi");Observer userWang = new User("WangWu");server.registerObserver(userZhang);server.registerObserver(userLi);server.registerObserver(userWang);server.setInfomation("PHP是世界上最好用的语言!");System.out.println("----------------------------------------------");server.removeObserver(userZhang);server.setInfomation("JAVA是世界上最好用的语言!");}
}

测试结果:

Android源码分析

1.RecycleView中使用的观察者模式

源码中RecyclerView怎么根据数据源的变更,让Item更新UI的呢?源码深入分析可以看这个

RecyclerView的观察者模式

2.ListView中的使用

ListView是Android中比较重要的一个控件,而ListView最重要的一个功能就是Adapter,通常,在我们往ListView添加数据的时候,都会调用Adapter的notifyDataSetChanged()方法来更新界面。对它的源码讲解可以看这篇

大致总结下它的过程,AdapterView中有一个内部类AdapterDataSetObserver,在ListView中设置Adapter时会构建一个AdapterDataSetObserver,并且注册到Adapter中,这就是一个观察者。而Adapter中包含一个数据集可观察者DataSetObservable,在数据数量发生变化的时候,开发者手动调用Adapter.notifyDataSetChanged,而它实际上会调用DataSetObservable的notifyChanged函数,该函数会遍历所有观察者的onChanged函数。在这个函数中会获取Adapter中数据集的新数量,然后调用ListView的requestLayout()方法重新布局,更新用户界面。

3.BroadcastReceiver中的使用

BroadcastReceiver是Android的四大组件之一,它是应用内,进程间的一种重要通信手段,它也是一个典型的发布–订阅模式,也就是观察者模式。具体内部实现方式可以看这篇文章BroadcastReceiver注册和发送过程

4.EventBus中的使用

EventBus也是基于观察者模式的。观察者模式的三个典型方法它都具有,即注册,取消注册,发送事件,简单流程如下所示:

//注册
EventBus.getDefault().register(Object subscriber);
//取消注册
EventBus.getDefault().unregister(Object subscriber);
//发送事件
EventBus.getDefault().post(Object event);

EventBus在我们平时项目开发过程中使用也很多,它是线程间,页面间通信的一种常用框架,可以大大简化页面间的耦合,用户只需要通过post发送事件,而接受方只需要通过register注册事件,就可以进行通信了。它也是观察者涉及模式,具体的内部实现可以看这里

比较常用的还有Otto,但是它的效率比不上EventBus,以及AndroidEventBus,使用得也很方便,但是效率比不上EventBus,框架的内部大多使用得观察者设计模式。通过查看它们的源码可以加深我们对设计模式的理解。

5.RxJava

Rxjava是一个经典的开源库,其中涉及到的观察者模式值得深入研究。

RxJava最核心的两个东西是Observables(被观察者,事件源)和Subscribers(观察者),Observables发出一系列事件,Subscribers处理这些事件。而RxJavaObservables是扩展自设计模式中的观察者模式。

简单流程如下所示:

//1.创建一个被观察者
Observable<String> myObservable = Observable.create(  new Observable.OnSubscribe<String>() {  @Override  public void call(Subscriber<? super String> sub) {  sub.onNext("Hello, world!");  sub.onCompleted();  }  }
);//2.创建一个观察者,也就是订阅者
Subscriber<String> mySubscriber = new Subscriber<String>() {  @Override  public void onNext(String s) { System.out.println(s); }  @Override  public void onCompleted() { }  @Override  public void onError(Throwable e) { }
};//3.观察者进行事件的订阅
myObservable.subscribe(mySubscriber);

更多源码,请到RxJava中进一步了解。

6.其他

如Android 中 View 的点击监听器的实现。

View 是被观察者,OnClickListener 对象是观察者,Activity 要如何知道 View 被点击了?

那就是构造一个 OnClickListener 对象,通过 setOnClickListener 与View达成一个订阅关系,一旦 View 被点击了,就通过OnClickListener对象的 OnClick 方法传达给 Activity 。采用观察者模式可以避免去轮询检查,节约有限的cpu资源。

观察者模式的优点与缺点

优点

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

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

缺点

  • 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时

  • 观察者知道被观察者发送通知了,但是观察者不知道所观察的对象具体是如何发生变化的

  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃

在项目中的实际运用(实战)

在项目中我们用到了美团技术团队开源的LiveDataBus新的消息总线框架,并进行了二次封装。订阅者可以订阅某个消息通道的消息,发布者可以把消息发布到消息通道上。利用LiveDataBus,不仅可以实现消息总线功能,而且对于订阅者,他们不需要关心何时取消订阅,极大减少了因为忘记取消订阅造成的内存泄漏风险。

调用流程如下:

1.注册订阅
LiveDataBus.instance.observe(this, EventKey.FINISH_LOGIN_ACTIVITY, Observer<String> {//doSomething()  })
2.发送消息:LiveDataBus.instance.setValue(EventKey.FINISH_LOGIN_ACTIVITY, "")

我们发送了一个名为EventKey.FINISH_LOGIN_ACTIVITY,值为空的事件。
这个时候订阅者就会收到消息,并作相应的处理,非常简单。

后期我会整理好二次封装的LiveDataBus消息总线工具类代码到github中的代码库android-common-project,敬请关注,如有疑问和不足之处,欢迎交流。

总结

观察者模式主要的作用是对象解耦,将观察者和被观察者完全隔离。在Android开发中运用场景也非常广泛,值得深入研究和总结。

参考资料:

1.人人都会设计模式:观察者模式–Observer

2.Android开源框架源码鉴赏:EventBus

3.ListView 源码详解 有这一篇就够了

4.JAVA设计模式之观察者模式

5.Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus
6.何红辉,关爱民. Android 源码设计模式解析与实战[M]. 北京:人民邮电出版社

Android设计模式之观察者模式在项目中的实际使用总结相关推荐

  1. 使用zinnia制作android手写输入功能(下)-------------------在项目中使用zinnia

    使用zinnia制作android手写输入功能(下)-------------------在项目中使用zinnia

  2. Android设计模式之——观察者模式

    一.介绍 观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统.订阅--发布系统.因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖.以G ...

  3. 设计模式之观察者模式在Listview中的应用

    有时候我们会有这么一个需求,在Listview的某个Item上有个按钮,点击这个按钮之后呢,需要对其它的item做一些操作,就像下面这个: 采纳按钮点击之前:采纳按钮点击之后: 简单介绍一下这两张图的 ...

  4. android图片缓存,直接应用项目中的Android图片缓存技术

    前不久搞的Android图片缓存,刚开始引入开源的框架,用着还行,但是在开发中遇到问题,就比如universal-image-loader-1.9.5.jar这个框架吧,在加载图片的时候自定义imag ...

  5. java design按钮_DesignJava 设计模式,讲述 的各种 方便在项目中进行 框架结构 Develop 238万源代码下载- www.pudn.com...

    文件名称: DesignJava下载 收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 1675 KB 上传时间: 2013-11-21 下载次数: 2 提 供 者: 1 ...

  6. Android项目中多次操作SharedPreferences导致ANR场景的解决

    系列文章目录 Android项目中多次操作SharedPreferences导致ANR场景的解决 文章目录 系列文章目录 项目背景: 以定位来获取广告的方式为例: 所遇到的挑战: 解决问题的步骤: 问 ...

  7. java 观察者模式_图解Java设计模式之观察者模式

    图解Java设计模式之观察者模式 天气预报项目需求 天气预报设计方案 1 - 普通方案 观察者模式(Observer)原理 观察者模式解决天气预报需求 观察者模式在JDK应用的源码分析 天气预报项目需 ...

  8. Android开发之将AndroidX项目改为非androidX(android.v7.support)的方法

    老套路上图:看看我目前AndroidX依赖的库文件 修改方法: 1.打开gradle.properties文件将文件里面的下面两个属性 android.useAndroidX=true android ...

  9. Android琐碎知识点,不断更新中

    1.获得屏幕的高度和宽度 width=context,getResources().getDisplayMetrics().widthPixels; height=context.getResourc ...

最新文章

  1. 一步步玩pcDuino3--mmc下的裸机流水灯
  2. 蓝桥杯C++ AB组辅导课 第六讲 双指针、BFS与图论 Acwing
  3. 1970 matla 时间_关于matlab:UTC到字符串的转换时间
  4. 【消息果留言板 v1.2】支持回复邮件提醒+页面pc端宽度调整+时间友好显示
  5. HibernateDaoSupport类的使用(转)
  6. Linux文件中批量转换时间戳,linux 文件日期转时间戳
  7. Linux学习之dpkg错误:另外一个进程已为dpkg状态库加锁
  8. 简易vbs脚本实现在浏览器自动刷新网页。
  9. php smtp.163 端口号,常用的邮箱服务器(SMTP、POP3)地址、端口
  10. 如何一周之内摸清一个行业?
  11. IE代理服务器没有响应 浏览器无法打开网页解决方法
  12. PDF格式转换WPS格式如何实现
  13. 有一个已经排好序的数组,输入一个数,将其插入到数组中,使得数组还是有序的。要求数组元素的值来自初始化
  14. 服务器声卡如何虚拟,服务器没声卡远程桌面连接怎么实现听到服务器的声音
  15. 支付宝小程序分享转发
  16. Office使用的窍门和小提示
  17. OTTO机器人之MAX7219点阵
  18. C语言——gotoxy()函数
  19. Java实现智能语音朗读(完整代码+EXE程序制作)
  20. 基于ibeacon蓝牙定位(微信小程序)

热门文章

  1. 亚马逊自养号如何操作测评更安全?
  2. java 异常 中文_Java中所有的运行时异常,带中文解释
  3. 新型高强度铝合金焊条
  4. 了解Wi-Fi信号强度【一文看懂】
  5. Java AES加密,兼容Linux和Windows
  6. 又一 VSCode 神器诞生!
  7. ubuntu 服务器无网络,服务器通过USB使用手机USB共享流量
  8. Architecture(X):哈弗架构和冯诺依曼架构
  9. DELL电脑window10系统怎么关闭F11和F12调整亮度功能
  10. Mysql——FROM_UNIXTIME和UNIX_TIMESTAMP函数的用法