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

版权声明:本文为博主原创文章,欢迎转载,转载请注明出处。 https://blog.csdn.net/jun5753/article/details/89455771

前言

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

观察者模式定义

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

观察者模式使用场景

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

  • 事件多级触发场景;

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

    具体场景如下:

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>();
    }
    
    @Override
    public void registerObserver(Observer o) {
        list.add(o);
    }
    
    @Override
    public void removeObserver(Observer o) {
        if(!list.isEmpty())
            list.remove(o);
    }

    //遍历
    @Override
    public 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;
    }
    
    @Override
    public 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]. 北京:人民邮电出版社

猜你喜欢

转载自blog.csdn.net/jun5753/article/details/89455771