0x2、观察者模式的推与拉
推方式
被观察者 → 观察者推送主题的
详细信息
(通常是被观察者的全部或部分数据),不管观察者是否需要。
拉方式
被观察者 → 观察者,只传递
少量信息
,如果观察者需要更详细的信息,可主动到被观察者中获取,一般的实现方式是被观察者自身通过update()方法传递给观察者,观察者再通过这个实例按需获取。
推方式是假定被观察者知道观察者所需的数据,拉方式是被观察者不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让其按照自己所需取值。
0x3、Java中对观察者模式的支持
Java的java.util包中,提供了一个 Observable
类和 Observer
接口,让我们可以更便捷地实现观察者模式。
核心用法:被观察者实现继承Observable,观察者实现Observer接口,通知变化时,调用setChange方法
简单的代码示例如下:
// 被观察者 import java.util.Observable; import java.util.Observer; public class CodingBoy extends Observable { private String title; private String contentUrl; public String getTitle() { return title; } public String getContentUrl() { return contentUrl; } public void update(String title, String url) { this.title = title; this.contentUrl = url; System.out.println("抠腚男孩公众号更新了文章:" + title); this.setChanged(); // 必不可少,通知改变 this.notifyObservers(this); // 这里用拉方式 } } // 观察者 public class Fan implements Observer { private String name; public Fan(String name) { this.name = name; } @Override public void update(Observable o, Object arg) { // 拉方式,通过实例按需获取所需信息 CodingBoy codingBoy = (CodingBoy) arg; System.out.println("粉丝【" + name + "】收到公号文章更新推送[" + codingBoy.getTitle() + "](" + codingBoy.getContentUrl() + ")"); } } // 测试用例 public class ClientTest { public static void main(String[] args) { CodingBoy codingBoy = new CodingBoy(); // 注册观察者 for (int i = 1; i < 4; i++) codingBoy.addObserver(new Fan(i + "")); codingBoy.update("《Python爬虫从入门到入狱》学习札记", "https://juejin.cn/post/6985093530473463816"); // 取消注册观察者 codingBoy.deleteObservers(); } }
代码运行输出结果如下:
非常简单~
0x4、加餐:模式应用实例 → EventBus源码解读
观察者模式在不同的场景与需求下,有不同的实现形式,我们上面的实现都属于 进程内同步堵塞,在一些需要快速响应的场景(如注册)就需要把实现方式改为 异步非堵塞,还有在跨进程的场景又得换成其他实现方式,如MQ。
此处剖析下Android中EventBus事务总线的源码(版本:3.1.1),了解下异步非堵塞实现方式的具体玩法~
EventBus的用法很简单:
// 注册订阅者 EventBus.getDefault().register(this); // 编写响应事件订阅方法,必须添加@Subscribe注解!!! @Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100) public void onMessageEvent(MessageEvent event) { } // 发送事件 EventBus.getDefault().post(new MessageEvent("Hello EventBus!")); EventBus.getDefault().postSticky(new MessageEvent("Hello EventBus!")); // 粘性事件 // 粘性事件所处理的问题:发布者先发送了事件,但此时订阅者还未产生, // 一段时间后订阅者才订阅该时间,就是使得发送事件后订阅者再订阅此时间也能收到该事件; // 解除注册 EventBus.getDefault().unregister(this);
① 初始化
先从 EventBus.getDefault() 入手,跟下:
线程安全带懒加载的 DCL单例
,实例不存在,调用构造方法初始化了Eventbus的一些配置,
② 订阅者订阅
getDefault()就是获得EventBus单例,往下走,跟 register(类实例)
:
subscriberMethodFinder.findSubscriberMethods()
返回了一个**SubscriberMethod
** 列表,跟下这个类:
了解完订阅方法,往下走,看下 findSubscriberMethods()
是怎么找的:
Tips:EventBus 3.0中提供了EventBusAnnotationProcessor注解处理器在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样比在运行时使用反射动态获取的速度快,所以ignoreGeneratedIndex默认为false~
findUsingInfo()
就是获取 MyEventBusIndex类
中的信息,遍历生成订阅者中订阅的方法数据,而 findUsingReflection()
:
跟下prepareFindState():