【设计模式】观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
基本介绍
-
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
-
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
-
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
-
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
-
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
-
应用实例:
- 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
- 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
-
优点:
- 1、观察者和被观察者是抽象耦合的。
- 2、建立一套触发机制。
-
缺点:
- 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
-
使用场景:
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项:
- 1、JAVA 中已经有了对观察者模式的支持类。
- 2、避免循环引用。
- 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
概括
观察者模式原理
类图:
角色分析:
- Subject:观察的主题
- registerObserver():注册观察者
- removeObserver():移除观察者
- notifyObservers():通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
- Observer:观察者,观察相应主题,当主题有所变化,接收来自主题的变化数据。
- Data:具体的观察数据
- ObserverA、B:具体的观察者
职务:
- 观察者模式类似订牛奶业务
- 奶站/气象局:Subject
- 用户/第三方网站:Observer
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject 通知 Observer 变化,比如这里的奶站是 Subject,是 1 的一方。用户时 Observer,是多的一方。
我的理解
提到回调,或者提到观察者模式,首先可以想到,这里面涉及到两个角色:观察者和被观察者。
被观察者负责在某一事件发生时调用回调函数通知观察者,观察者负责提供回调函数。
回调函数例子:
被观察者
sendMessage(msg) {
...
// 上面是各种业务逻辑
// 业务逻辑执行完以后,执行回调函数
Observer.onMessage(msg);
}
观察者
onMessage(msg) {
// 这个函数就能在被观察者发送消息的时候,得到消息,回调进行处理。
}
观察者模式面向的需求是:A 对象(观察者)对 B 对象(被观察者)的某种变化高度敏感,需要在 B 变化的一瞬间做出反应。举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者,小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会漏过任何瞬间。
程序的观察者模式和这种真正的『观察』略有不同,观察者不需要时刻盯着被观察者(例如 A 不需要每过 2ms 就检查一次 B 的状态),而是采用注册(Register)或者称为订阅(Subscribe)的方式,告诉被观察者:我需要你的某某状态,你要在它变化的时候通知我。
Android 开发中一个比较典型的例子是点击监听器 OnClickListener 。对设置 OnClickListener 来说, View 是被观察者, OnClickListener 是观察者,二者通过 setOnClickListener() 方法达成订阅关系。订阅之后用户点击按钮的瞬间,Android Framework 就会将点击事件发送给已经注册的 OnClickListener 。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。
当然,这也得益于我们可以随意定制自己程序中的观察者和被观察者,而警察叔叔明显无法要求小偷『你在作案的时候务必通知我』。
理解:其实也可以这么想,被观察者做了某件事,这个状态通过发送光信息,传达到观察者的眼睛里。
即 被观察者 发送信息给 观察者。
就相当于间谍、私家侦探,观察当一个对象一有什么风吹草动,就通知依赖他的对象。
比如说 A班班主任是张三,一说到张三他就是A班班主任,一说到A班班主任那就是张三,这俩是依赖的关系。如果A班班主任变成了李四,那么张三的职位也要改变,他不能再是一班班主任了。所以得有人通知,上面得发布文件,说A班班主任修改为李四,张三修改去B班当班主任。
想知道咱们公司最新 MM 情报吗?加入公司的 MM 情报邮件组就行了,tom 负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦。
观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
应用实例
天气预报项目需求,具体要求如下:
- 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
- 需要设计开放型 API,便于其他第三方也能接入气象站获取数据。
- 提供温度、气压和湿度的接口
- 测量数据更新时,要能实时的通知给第三方
使用传统方式
WeatherData 类
传统的设计方案
代码实现:
CurrentConditions
package com.nemo.observer;
/**
*显示当前天气情况(可以理解成是气象站自己的网站)
*@author Administrator
*/
public class CurrentConditions {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
//更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
public void display() {
System.out.println("***Today mTemperature: " + temperature + "***");
System.out.println("***Today mPressure: " + pressure + "***");
System.out.println("***Today mHumidity: " + humidity + "***");
}
}
WeatherData
package com.nemo.observer;
/**
*类是核心
*1. 包含最新的天气情况信息
*2. 含有 CurrentConditions 对象
*3. 当数据有更新时,就主动的调用 CurrentConditions 对象 update 方法(含 display), 这样他们(接入方)就看到最新的信息
*@author Administrator
*/
public class WeatherData {
private float temperatrue;
private float pressure;
private float humidity;
private CurrentConditions currentConditions;
//加入新的第三方
public WeatherData(CurrentConditions currentConditions) {
this.currentConditions = currentConditions;
}
public float getTemperature() {
return temperatrue;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
//调用 接入方的 update
currentConditions.update(getTemperature(), getPressure(), getHumidity());
}
//当数据有更新时,就调用 setData
public void setData(float temperature, float pressure, float humidity) {
this.temperatrue = temperature;
this.pressure = pressure; this.humidity = humidity;
//调用 dataChange,将最新的信息 推送给 接入方 currentConditions
dataChange();
}
}
Client
package com.nemo.observer;
public class Client {
public static void main(String[] args) {
//创建接入方 currentConditions
CurrentConditions currentConditions = new CurrentConditions();
//创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData 中
WeatherData weatherData = new WeatherData(currentConditions);
//更新天气情况
weatherData.setData(30, 150, 40);
//天气情况变化
System.out.println("============天气情况变化=============");
weatherData.setData(40, 160, 20);
}
}
问题分析
- 其他第三方接入气象站获取数据的问题
- 无法在运行时动态的添加第三方 (新浪网站)
- 违反 ocp 原则=>观察者模式
//在 WeatherData 中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到 dataChange, 不利于维护,也不是动态加入
public void dataChange() {
currentConditions.update(getTemperature(), getPressure(), getHumidity());
}
使用观察者模式
类图说明
代码实现
Subject
package com.nemo.observer.improve;
//接口, 让 WeatherData 来实现
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
Observer
package com.nemo.observer.improve;
//观察者接口,有观察者来实现
public interface Observer {
public void update(float temperature, float pressure, float humidity);
}
BaiduSite
package com.nemo.observer.improve;
public class BaiduSite implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
// 显示
public void display() {
System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***");
System.out.println("***百度网站 湿度: " + humidity + "***");
}
}
CurrentConditions
package com.nemo.observer.improve;
public class CurrentConditions implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity; display();
}
// 显示
public void display() {
System.out.println("***Today mTemperature: " + temperature + "***");
System.out.println("***Today mPressure: " + pressure + "***");
System.out.println("***Today mHumidity: " + humidity + "***");
}
}
WeatherData
package com.nemo.observer.improve;
import java.util.ArrayList;
/**
*类是核心
*1. 包含最新的天气情况信息
*2. 含有 观察者集合,使用 ArrayList 管理
*3. 当数据有更新时,就主动的调用 ArrayList, 通知所有的(接入方)就看到最新的信息
*@author Administrator
*/
public class WeatherData implements Subject {
private float temperatrue;
private float pressure;
private float humidity;
//观察者集合
private ArrayList<Observer> observers;
//加入新的第三方
public WeatherData() {
observers = new ArrayList<Observer>();
}
public float getTemperature() {
return temperatrue;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
//调用 接入方的 update
notifyObservers();
}
//当数据有更新时,就调用 setData
public void setData(float temperature, float pressure, float humidity) {
this.temperatrue = temperature;
this.pressure = pressure;
this.humidity = humidity;
//调用 dataChange,将最新的信息 推送给 接入方 currentConditions
dataChange();
}
//注册一个观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
//移除一个观察者
@Override
public void removeObserver(Observer o) {
if(observers.contains(o)) {
observers.remove(o);
}
}
//遍历所有的观察者,并通知
@Override
public void notifyObservers() {
for(int i = 0; i < observers.size(); i++) {
observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
}
}
}
Client
package com.nemo.observer.improve;
public class Client {
public static void main(String[] args) {
//创建一个 WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
BaiduSite baiduSite = new BaiduSite();
// 注册到 weatherData
weatherData.registerObserver(currentConditions);
weatherData.registerObserver(baiduSite);
// 测试
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
weatherData.removeObserver(currentConditions);
//测试
System.out.println();
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
}
}
观察者模式的好处
- 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
- 这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码, 遵守了 ocp 原则。
应用实例
观察者模式在 Jdk 应用的源码分析
- Jdk 的 Observable 类就使用了观察者模式
- 代码分析+模式角色分析
- 模式角色分析
- Observable 的作用和地位等价于 我们前面讲过 Subject
- Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer 的方法 add.. delete .. notify...
- Observer 的作用和地位等价于我们前面讲过的 Observer, 有 update
- Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式
Java11 基于Flow.Publisher/Flow.Subscriber的观察者模式
此Demo代码由三部分组成,分别是发布者、观察者、执行函数
用法
类Flow.Publisher用法
订阅者收到的项目(和相关控制消息)的生产者。 除非遇到丢失或错误,否则每个当前Flow.Subscriber
以相同的顺序接收相同的项目(通过方法onNext
)。 如果发布者遇到不允许将项目发布到订阅服务器的错误,则该订阅服务器将收到onError
,然后不再接收任何消息。 否则,当知道不再向其发出消息时,订户接收onComplete
。 发布者确保每个订阅的订阅者方法调用严格按happens-before顺序排序。
出版商可能会在政策方面有所不同,关于是否因为资源限制而导致项目失败(未能发布项目)被视为不可恢复的错误。 发布商还可能会对订阅者是否收到订阅前生成或可用的项目有所不同。
如果可能,添加给定的订阅者。 如果可能,添加给定的订阅者。 如果已经认购,或认购的尝试失败,因为违反政策或错误,用户的onError
方法被调用与IllegalStateException
。 否则,将使用新的Flow.Subscription
调用订阅者的onSubscribe
方法。 订户可以使通过调用接收项目request
这个订阅的方法,并且可以通过调用其退订cancel
方法。
变量和类型 | 方法 | 描述 |
---|---|---|
void | subscribe(Flow.Subscriber<? super T> subscriber) |
如果可能,添加给定的订阅者。 |
创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来,整条链子就可以工作了。代码形式很简单:
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);
特别注意:有人可能会注意到, subscribe() 这个方法有点怪:它看起来是『observalbe 订阅了 observer / subscriber』而不是『observer / subscriber 订阅了 observalbe』,这看起来就像『杂志订阅了读者』一样颠倒了对象关系。这让人读起来有点别扭,不过如果把 API 设计成 observer.subscribe(observable) / subscriber.subscribe(observable) ,虽然更加符合思维逻辑,但对流式 API 的设计就造成影响了,比较起来明显是得不偿失的。
Observable.subscribe(Subscriber) 的内部实现是这样的(仅核心代码):
// 注意:这不是 subscribe() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
可以看到,subscriber() 做了3件事:
调用 Subscriber.onStart() 。这个方法在前面已经介绍过,是一个可选的准备方法。
调用 Observable 中的 OnSubscribe.call(Subscriber) 。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中, Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当 subscribe() 方法执行的时候。
将传入的 Subscriber 作为 Subscription 返回。这是为了方便 unsubscribe().
整个过程中对象间的关系如下图:
类Flow.Subscriber用法
消息的接收者。 对于每个Flow.Subscription
,此接口中的方法按严格顺序调用。 当已知对于尚未由错误终止的订阅不会发生其他订阅者方法调用时调用的方法,之后订阅不会调用其他订阅者方法。 在发布者或订阅服务器遇到不可恢复的错误时调用的方法,之后订阅服务器不会调用其他订阅服务器方法。 使用Subscription的下一个项目调用的方法。 在为给定的Subscription调用任何其他Subscriber方法之前调用的方法。 在为给定的Subscription调用任何其他Subscriber方法之前调用的方法。 如果此方法抛出异常,则不保证结果行为,但可能导致不建立或取消订阅。
通常,此方法的实现调用subscription.request
以启用接收项。
使用Subscription的下一个项目调用的方法。 如果此方法抛出异常,则无法保证生成的行为,但可能导致取消订阅。 在发布者或订阅服务器遇到不可恢复的错误时调用的方法,之后订阅服务器不会调用其他订阅服务器方法。 如果此方法本身抛出异常,则结果行为未定义。 当已知对于尚未由错误终止的订阅不会发生其他订阅者方法调用时调用的方法,之后订阅不会调用其他订阅者方法。 如果此方法抛出异常,则结果行为未定义。
变量和类型 | 方法 | 描述 |
---|---|---|
void | onComplete() | 当已知对于尚未由错误终止的订阅不会发生其他订阅者方法调用时调用的方法,之后订阅不会调用其他订阅者方法。 |
void | onError(Throwable throwable) | 在发布者或订阅服务器遇到不可恢复的错误时调用的方法,之后订阅服务器不会调用其他订阅服务器方法。 |
void | onNext(T item) | 使用Subscription的下一个项目调用的方法。 |
void | onSubscribe(Flow.Subscription subscription) | 在为给定的Subscription调用任何其他Subscriber方法之前调用的方法。 |
类Flow.Subscription用法
消息控制链接Flow.Publisher
和Flow.Subscriber
。 订阅者只有在被要求时才会收到项目,并且可以随时取消。 此接口中的方法仅由其订阅者调用; 其他环境中的用法具有不确定的效果。 导致订阅服务器(最终)停止接收消息。 将给定数量的项目n
添加到此订阅的当前未履行需求中。 将给定数量的项目n
添加到此订阅的当前未履行需求中。 如果n
小于或等于零,则订户将收到带有IllegalArgumentException
参数的onError
信号。 否则,订户将接收最多n
额外的onNext
调用(如果终止则更少)。
导致订阅者(最终)停止接收消息。实现是最好的——调用此方法后可能会收到额外的消息。被取消的订阅不需要接收onComplete
或onError
信号。
Causes the Subscriber to (eventually) stop receiving messages. Implementation is best-effort -- additional messages may be received after invoking this method. A cancelled subscription need not ever receive an onComplete or onError signal.
变量和类型 | 方法 | 描述 |
---|---|---|
void | cancel() | 导致订阅服务器(最终)停止接收消息。 |
void | request(long n) | 将给定数量的项目 n添加到此订阅的当前未履行需求中。 |
流程
要想理解 rxjava;
看大神:http://gank.io/post/560e15be2dca930e00da1083
RxJava是 ReactiveX 在 Java 上的开源的实现。RxJava可以轻松处理不同运行环境下的后台线程或UI线程任务的框架。RxJava的异步实现,是通过一种扩展的观察者模式来实现的。
Observable(可观察者,即被观察者) 和 Subscriber(订阅者)是两个主要的类。在 RxJava 上,一个 Observable 是一个发出数据流或者事件的类,Subscriber 是一个对这些发出的 items (数据流或者事件)进行处理(采取行动)的类。
Observable 和Observer 通过 subscribe() 方法实现订阅关系。一个 Observable 的标准流发出一个或多个item,然后成功完成或者出错。一个 Observable 可以有多个 Subscribers,并且通过 Observable 发出的每一个 item,该 item 将会被发送到 Subscriber.onNext() 方法来进行处理。一旦 Observable 不再发出 items,它将会调用 Subscriber.onCompleted() 方法,或如果有一个出错的话Observable 会调用 Subscriber.onError() 方法。
-
onNext(): (相当于 onClick() / onEvent())RxJava的事件回调方法,针对普通事件。
-
onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
-
onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。
-
在一个正确运行的事件序列中,onCompleted() 和 onError() 有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted() 和 onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。
RxJava 的观察者模式大致如下图:
发布者Sender实现Flow.Publisher
package design.mode.observer.pubsub;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
public class Sender implements Flow.Publisher {
private SubmissionPublisher<Object> publisher;
Sender(){
int THREAD_POOL_SIZE = 5;
ExecutorService EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
publisher = new SubmissionPublisher<>(EXECUTOR,Flow.defaultBufferSize());
}
@Override
public void subscribe(Flow.Subscriber subscriber) {
getPublisher().subscribe(subscriber);
}
public SubmissionPublisher<Object> getPublisher() {
return publisher;
}
public void send(Map<String,Object> map){
System.out.println("publish send msg : "+map);
getPublisher().submit(map);
}
}
接收者Receiver实现Flow.Subscriber
package design.mode.observer.pubsub;
import java.util.Map;
import java.util.concurrent.Flow;
public class Receiver implements Flow.Subscriber<Map<String,Object>> {
private Flow.Subscription subscription;
private int REQ;
Receiver(int req){
this.REQ=req;
}
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription=subscription;
this.subscription.request(REQ);
}
@Override
public void onNext(Map<String,Object> item) {
subscription.request(REQ); // 将给定数目n的项添加到此订阅的当前未满足的需求中。
System.out.println(REQ + " onNext "+ item.toString());
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onComplete() {
System.out.println(REQ + " onComplete");
}
}
Main函数验证结果
package design.mode.observer.pubsub;
import java.util.Map;
public class PubsubMain {
public static void main(String[] args) {
Sender sender = new Sender();
Receiver receiver1 = new Receiver(1);
sender.subscribe(receiver1);
Receiver receiver2 = new Receiver(2);
sender.subscribe(receiver2);
for (int i = 0; i < 10; i++) {
sender.send(Map.of(i+"","这是第"+i+"条消息"));
}
}
}
运行结果:
publish send msg : {0=这是第0条消息}
publish send msg : {1=这是第1条消息}
publish send msg : {2=这是第2条消息}
publish send msg : {3=这是第3条消息}
publish send msg : {4=这是第4条消息}
publish send msg : {5=这是第5条消息}
publish send msg : {6=这是第6条消息}
publish send msg : {7=这是第7条消息}
publish send msg : {8=这是第8条消息}
publish send msg : {9=这是第9条消息}
1 onNext {0=这是第0条消息}
2 onNext {0=这是第0条消息}
2 onNext {1=这是第1条消息}
1 onNext {1=这是第1条消息}
2 onNext {2=这是第2条消息}
1 onNext {2=这是第2条消息}
2 onNext {3=这是第3条消息}
1 onNext {3=这是第3条消息}
2 onNext {4=这是第4条消息}
1 onNext {4=这是第4条消息}
2 onNext {5=这是第5条消息}
2 onNext {6=这是第6条消息}
1 onNext {5=这是第5条消息}
2 onNext {7=这是第7条消息}
1 onNext {6=这是第6条消息}
2 onNext {8=这是第8条消息}
1 onNext {7=这是第7条消息}
2 onNext {9=这是第9条消息}
1 onNext {8=这是第8条消息}
1 onNext {9=这是第9条消息}
这里的运行结果需要注意,结果是多线程穿插进行的,所以任何顺序的结果都是有可能的,也可以一发出就被观察到。
publish send msg : {0=这是第0条消息}
1 onNext {0=这是第0条消息}
2 onNext {0=这是第0条消息}
publish send msg : {1=这是第1条消息}
1 onNext {1=这是第1条消息}
2 onNext {1=这是第1条消息}
publish send msg : {2=这是第2条消息}
1 onNext {2=这是第2条消息}
2 onNext {2=这是第2条消息}
publish send msg : {3=这是第3条消息}
1 onNext {3=这是第3条消息}
2 onNext {3=这是第3条消息}
publish send msg : {4=这是第4条消息}
2 onNext {4=这是第4条消息}
1 onNext {4=这是第4条消息}
publish send msg : {5=这是第5条消息}
1 onNext {5=这是第5条消息}
2 onNext {5=这是第5条消息}
publish send msg : {6=这是第6条消息}
1 onNext {6=这是第6条消息}
2 onNext {6=这是第6条消息}
publish send msg : {7=这是第7条消息}
2 onNext {7=这是第7条消息}
1 onNext {7=这是第7条消息}
publish send msg : {8=这是第8条消息}
1 onNext {8=这是第8条消息}
2 onNext {8=这是第8条消息}
publish send msg : {9=这是第9条消息}
2 onNext {9=这是第9条消息}
1 onNext {9=这是第9条消息}
gRPC框架
为了实现双向流
笔者将不定期更新【考研或就业】的专业相关知识以及自身理解,希望大家能【关注】我。
如果觉得对您有用,请点击左下角的【点赞】按钮,给我一些鼓励,谢谢!
如果有更好的理解或建议,请在【评论】中写出,我会及时修改,谢谢啦!
本文来自博客园,作者:Nemo&
转载请注明原文链接:https://www.cnblogs.com/blknemo/p/13258602.html