最近在学Rxjava,找到了一本很好的书《RxJava Essentials》,想将其翻译出来,有不妥之处还请指教。
第一章就不翻译了,直接上第二章。
RxJava要素(二)
第二章为什么是Observables
在面向对象的体系(Architectures)中,开发者努力去创建一系列的解耦实体(entities)。这样,实体(entities)可以被测试、重复使用、和维护而不会干扰到整个系统。设计这种类型的系统带来了一个微妙的(trickey)的副作用:维护相关对象的一致性。
第一个用来解决这个问题的模式是SmallTalk的Model_View_Controller(MVC)体系(architecture),用户接口框架(The user interface framework)提供了一种维持交互界面元素与包含有数据的真正个体分离的方法。与此同时,它也提供了一种方便的方法保持所有一切都同步(in sync)。
观察者模式是《Design Patterns:Elements of Reusable Object_Oriented Software》中经常被讨论的非常出名的设计模式之一。它属于行为模式并且提供了在一对多的依赖中绑定对象的方法:当一个对象发生改变时,所有依赖它的对象都会被通知并自动更新。
在本章中,我们将要对观察者模式进行一个概述,它是如何被Rxjava实现与扩展的,什么是Observable,以及Observables是如何与可迭代对象(Iterables)关联的。
观察者模式
现在,观察者模式在是最常见的软件设计模式之一。它建立在主题(subject)的概念上。主题(subject)是一个特别的对象,它保留了一个当主题(subject)改变时想被通知的对象的列表。这些对象被称为观察者(Observers),它暴露了了一个当主题的状态改变时用来唤醒的通知方法(Notification method)
在前面的章节中,我们看到了电子表格的例子(没翻译呢,,,)。现在我们扩展那个例子,展示一个更加复杂的场景。让我们来设想一张包含会计资料的电子表格。我们可以用表格,或者用3D直方图(3D-histogram),或者用饼状图来展示它的资料。所有这些表示都将依赖于同一个正在被展示的资料集。所有这些表示将是一个观察者,依赖于一个单独的主题,维持所有信息。
3D直方图(3D-histogram)类,饼状图类,表格类,和那些维持(maintaining)资料的类都完美解耦:他们可以被独立的使用与复用,但也可以一起工作。那些表示类相互之间并不了解,但他们表现的像了解一样:他们知道去哪找他们需要展示的信息,当被通知资料改变时他们知道必须要更新自己的资料展示。
下图描述了主题/观察者关系是怎样的一个一对多关系
上图展示了一个单个主题可以服务三个观察者。明显,没有理由去限制观察者的数量:如果有需要,一个主题应当可以有无数个观察者,并且当主题的状态改变时他们每个观察者都应被通知。
什么时候需要使用观察者模式
观察者模式很适合以下场景:
当你的架构(architecture)有两个实体(entities),相互依赖,并且你想保持他们分离从而来改变他们或者独立重复使用他们时;
当一个正在改变的对象必须要通知数量不明的关联对象关于自己的改变时;
当一个正在改变的对象在没有假定去通知哪些对象时必须要通知他们时(When a changing object has to notify other objects without making assumptions about who these objects are);
RxJava观察者模式工具套件(toolkit)
在RxJava的世界里,我们有四个主要参与者(players):
Observable
Observer
Subscriber
Subjects
Observables和Subjects是两个生产(producting)实体(entities),Observers和Subscribers是两个消费(consuming)实体(entities)。
Observable
当我们必须要执行一些带有较低复杂度(with a lite level of complexity)的异步操作时,Java提供了一些经典的类,比如:Thread、Future、FutureTask、CompletableFuture,去解决这些问题。当复杂等级增加时,这些解决方案往往变得混乱并且难以维护,总而言之,他们不能被约束(chained)。
RxJava Observable被设计用来解决这些问题。他们灵活并且易于使用,他们能被约束(chained),并且他们可以从事单个结果例程(result routine),甚至数列(sequences)。无论何时你想要发射一个标量(scalar)值或者一系列值,甚至是一个无限值流(infinite value stream),你都可以使用Observable。
Observable的生命周期包含三个我们很容易和迭代(Iterable)的生命周期事件比较的可能事件。下表展示了Observable 异步/推送(async/push)方法与迭代同步/拉(sync/pull)
在迭代(Iterable)中,消费者从生产者同步取出值并且当前线程会一直阻塞直到这些值到达。与此相反,在Observable中,无论何时当值可用时,生产者异步推送值给Observer。这种方法更复杂,因为即使值同步或异步到达时,消费者可以根据在两种场景期望中做出相应行为。
为了合适的复制迭代(Iterable)接口,RxJava Observable类增强了由The Gang of Four所述的观察者模式的基本语义,并且引入了两种新的能力(abilities):
onCompleted(),这是一种通知Observer没有更多来自Observable资料的能力(ability);
onError(),这是一种通知发生了错误的能力(ability);
记住这两种能力(abilities)和前面的表格。我们知道Observables和迭代(Iterables)有可比的能力集但不同的数据流方向:推送(push) VS 拉(pull)。
热的(Hot)与冷的(Cold)Observable
从一个发射点的视角来看,有两种不同的Observables:热的(hot)与冷的(cold)。一个热的Observable,通常从创建后就发射项目(items),因此任何订阅该Observable的Observer可能在中途观察结果。一个冷的Observable会一直等待直到一个Observer订阅了它后才发射,这样的一个Observer可以保证从一开始看到整个结果。
创建Observable
在接下来章节中将讨论Observable类提供创建Observable的方法。
Observable.create()
create()方法给了开发者从无到有去创建Observable的的能力。它需要一个OnSubscribe对象作为参数,该对象扩展于Action1并且当Observer订阅我们的Observable时它会执行call()方法:
Observable.create(new Observable.onSubscribe<Object>(){
@Override
public void call(Subscriber<? super Object> subscriber){
}
});
Observable与Observer通过subscriber变量和通过调用当前环境相应的方法来交流,让我们来看一个现实世界的例子:
Observable<Integer> observableString = Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
for (int i = 0; i < 5; i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
});
Subscription subscription = observableString.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
System.out.println("Observable completed");
}
@Override
public void onError(Throwable throwable) {
System.out.println("Oh no! Something wrong happened!");
}
@Override
public void onNext(Integer item) {
System.out.println("Items is" + item);
}
});
上述例子故意简单点,毕竟这是你第一次看见Rxjava运行。我希望你有能够猜出将要发生什么。
我们创建了一个新的Observable<Integer>执行五个元素循环的项目,一个一个的发射他们,然后结束它。
在另外一处,我们订阅了该Observable,得到了一个Subscription。从我们订阅的那一刻起,我们就开始接收整数值并且将他们一个个打印出来。我们不知道将要接收多少个整数。实际上,我们无需知道,因为我们为各种场景提供了行为:
如果我们接收到整数,我们就打印出它们;
如果该序列结束了,我们打印序列关闭的消息;
如果发生了错误,我们打印出出错消息。
Observable.from()
在前面的例子中,我们创建了一个整数序列并且一个一个的发射他们。如果我们已经有一个list呢?我们能保存那个for循环但仍然一个一个发射他们吗?
在接写来的代码例子中,我们将从一个我们拥有的list中创建一个Observable序列:
List<Integer> items = new ArrayList<Integer>();
items.add(1);
items.add(10);
items.add(100);
items.add(200);
Observable<Integer> observableString = Observable.from(items);
observableString.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
System.out.println("Observable completed");
}
@Override
public void onError(Throwable throwable) {
System.out.println("Oh no!Something wrong happened!");
}
@Override
public void onNext(Integer item) {
System.out.println("item is" + item) ;
}
});
输出毫无疑问与前面的例子是相同的。
from()构造者可以创建一个Observable从list/array,一个一个发射包含在list/array中的或者发射来自Future对象的.get()方法的结果值。将Future作为一个参数,我们可以指定一个超时值。 当前的Observable将会等待来自Future的结果,如果在时间超出之前结果没出来,当前的Observable将会调用onError()方法并且通知Observer发生了错误。
Observa.just()
如果我们有一个经典的Java方法并且想将它转化成一个Observa时该怎么办?正如我们已经看见的那样,可以使用create()方法,或者像以下那样,我们可以避免一大堆的样板代码:
Observable<String> observableString = Observable.just(helloWorld());
observableString.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("Observable completed");
}
@Override
public void onError(Throwable throwable) {
System.out.println("Oh no!Something wrong happened!");
}
@Override
public void onNext(String message) {
System.out.println("item is" + message) ;
}
});
helloWorld()的功能相当简单,就像这样:
private String helloWorld(){
return "Hello World";
}
当然,它可能具有任何我们想要的功能。在前面的例子中,在我们创建Observable对象的时候,just()执行了它的功能,当我们订阅它的时候,它发射了返回值。
just()构造者可以接收1到9个参数,Observable将会按照参数顺序发射它们,just()也接受lists或者arrays,就像from()一样,但是它不会遍历list,发射每个值,它将仅仅发射整个list。通常,它被用来发射一个定义了的值集,但是如果我们的功能不是时间变量,我们可以使用just()来获取一个更有组织和可测试的代码库。
关于just()创建者最后一个需要注意的是,在发射完值后,当前Observable会自然终止。对于前面的例子,我们将会在系统控制台收到两条信息:“Hello World”和“Observable completed”。
Observable.empty(),Observable.never(),和Observable.throw()
如果出于某种原因我们需要一个不发射任何东西但是正常终止的Observable,我们可以使用empty()。我们可以使用never()去创建一个不发射任何东西并且永不终止的Observable;我们可以使用throw()去创建一个不发射任何东西并且以出错为终止的Observable。
Subject = Observable + Observer
Subject是一个可以同时既充当Observer又可充当Observable的奇特对象:它充当连接两个世界的桥梁。Subject可以订阅一个Observable,这时它表现得像一个Observer,但当它可以发射新的项目或者甚至传递接收的项目时,此时就表现得像Observable。明显,作为一个Observable、Observer或者其他的主题可以去订阅它。
当subject订阅Observable时,它将会触发Observable发射,如果原Observable是冷的,这将会使得原来的冷Observable的返回的subject变成热的Observable变量。
RxJava提供了四种不同的subject类型:
PublishSubject
BehaviorSubject
ReplaySubject
AsyncSubject
PublishSubject
PublishSubject是一个基本的Subject对象,让我们来看一个经典的用PublicSubject实现的Observable ——Hello World
PublishSubject stringPublishSubject = PublishSubject.create();
Subscription subscriptionPrint = stringPublishSubject.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("Observable completed");
}
@Override
public void onError(Throwable throwable) {
System.out.println("Oh no!Something wrong happened!");
}
@Override
public void onNext(String message) {
System.out.println("item is" + message);
}
});
stringPublishSubject.onNext("Hello World");(点击查看例子)
在上面的例子中,我们创建了一个使用create()方法发射String值PublishSubject,然后我们订阅了该PublishSubject。在此时,没有一个项目被发射,因此我们的Observer一直在等待,没有堵塞或者消耗资源。它就在那等待来自Subject的值。如果Subject不发射一个值我们的Observer将会永久等待。但我们不必担心:当前Observer知道在每种场景下该做什么。什么时候我们不关心因为它是响应式的:系统将会响应。我们不关心它何时响应。我们只关心当它响应时将会发生什么。
最后一行代码展示了手动发射我们的“Hello World"字符串,它将触发当前的Observer的onNext()方法并且让我们去打印”Hello World“信息到控制台。
让我们看一个更复杂的例子。假设我们有一个私有的Observable并且从外面不可访问。这个Observable发射值在它整个生命周期里。我们不关心这些值,我们只关心他们的终止。
首先,我们创建一个新的通过onNext()反应的并且从外部可以访问的PublishSubject:
final PublishSubject subject = PublishSubject.create();
subject.subscribe(new Observer<Boolean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Boolean completed) {
System.out.println("Observable completed");
}
});
然后我们创建一个私有的Observable,只有Subject有访问权:
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
for (int i = 0;i < 5;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).doOnCompleted(new Action0() {
@Override
public void call() {
subject.onNext(true);
}
}).subscribe();
Observable.create()方法包含了 我们熟悉的发射数字的for循环。doOnCompleted()指定了当当前Observable终止时将要发生什么:在Subject上发射true。最后我们订阅了Observable。明显,空的subscribe()调用仅仅启动了Observable,忽略所有的发射值,完成或错误事件。在该例中我们需要它这样。
该例中,我们创建了一个可以连接到Observables并且同时可以被观察的实体,当我们想创建一个分开的,抽象的或者一个更好观察公共资源的点,这相当有用。
BehaviorSubject
基本上,BehaviorSubject是一个发射最近观察的项目(可能是OnNext()或者OnError()或者OnCompleted(),具体请点击下面的链接)和所有随后观察的项目所订阅的项目:
BehaviorSubject<Integer> behaviorSubject = BehaviorSubject.create(1);(点击查看例子)
在这个简短的例子中,我们创建了一个发射整数的BehaviorSubject。由于它要发射当Observe订阅时的最近值,所以BehaviorSubject需要一个初始值。
ReplaySubject
ReplaySubject缓存所有它观察的项目并且向所有订阅它的Observer展示:
ReplaySubject<Integer> replaySubject = ReplaySubject.create();(点击查看例子)
AsyncSubject
当Observable完成的时候,AsyncSubject仅展出最后订阅它的Observer最后被其观察到的项目:
AsyncSubject asyncSubject = AsyncSubject.create();(点击查看例子)
由于时间匆忙,如有出错希望各位能指正,本人将及时改正。谢谢