理解RxJava:(一)基础知识
本文翻译自Grokking RxJava, Part 1: The Basics,著作权归原作者danlew所有。译文由JohnTsai翻译。转载请注明出处,并保留此段声明。
RxJava这些天成为了Android开发者关注的新热点。唯一的问题是它在你刚接触时难以理解。当你习惯了命令式编程,函数响应式编程就变得难以理解。但是一旦你理解了它,它就变得很棒了。
我在这试着给你们带来不一样的RxJava。这一系列四篇文章的目标是带你们入门。我不会也不能讲解所有的东西。我只是想让你们对RxJava以及它的工作原理感兴趣。
基础知识
响应式代码的基本构成部分是Observables
和Subscribers
(译者注:技术名词很难找到合适的中文翻译,所以维持原文不被翻译)。Observable
发出items,Subscriber
消费这些items。
items如何被消费有一套规则。Observable
发出任意数量的items(包括0个items),要么以成功完成终止,要么以发生错误终止。对于Observable
的每个Subscriber
,Observable
调用Subscriber.onNext()
方法任意次,然后调用Subscriber.onComplete()
方法或Subscriber.onError()
方法。
这看起来和我们用的观察者模式类似,但在一个关键地方不同——Observables
在有人明确地订阅它之后才会开始发出items。换句话说,没有人去订阅,就不会发出订阅事件(译者注:引申自If a tree falls in a forest)。
Hello World
让我们通过一个具体例子来看RxJava是如何运作的。首先,先创建一个基本的Observable
:
Observable myObservable = Observable.create(
new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> sub) {
sub.onNext("Hello, world!");
sub.onCompleted();
}
}
);
Observable
发出Hello World
然后完成。现在创建一个Subscriber
来消费掉数据。
Subscriber mySubscriber = new Subscriber() {
@Override
public void onNext(String s) { System.out.println(s); }
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
所有这些所做的是将Observable
发出的每个String打印出来。
现在有了myObservable
和mySubscriber
,我们可以用subscribe()
方法将它们连接起来。
myObservable.subscribe(mySubscriber);
// 输出 "Hello, world!"
当订阅发生时,myobservable
调用subsriber
的onNext()
和onComplete()
方法。作为结果,mySubscriber
输出"Hello,World"
然后结束。
更简洁的代码
为了输出"Hello,World!"
,上面写了许多样板代码。这是因为我为了让你们能够明确发生了什么,选择了一种啰嗦的方式。RxJava提供了许多快捷写法让我们能写出更简洁的代码。
首先,简化Observable
。RxJava有针对通用任务的多种内置Observable
构建方法。在这种情况下,Observable.just()
发出一个item然后完成结束,就像我们上面的代码:
Observable myObservable =
Observable.just("Hello, world!");
然后,对于啰嗦的Subscriber
。我们不关心onCompleted()
和onError()
方法,取而代之,我们可以用一个更简洁的类来定义在onNext()
中做什么:
Action1 onNextAction = new Action1() {
@Override
public void call(String s) {
System.out.println(s);
}
};
Subscriber
每部分的Action都能自定义。Observable.subscribe()
能处理一个,两个以及三个Action
参数,以取代onNext()
,onError()
和onComplete()
方法。复用我们之前的Subscriber
,如下:
myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);
然而,我们仅仅需要第一个参数,因为我们可以省略onError()
和onComplete()
方法:
myObservable.subscribe(onNextAction);
// 输出 "Hello, world!"
现在,让我们通过方法的链式调用来取代这些变量:
Observable.just("Hello, world!")
.subscribe(new Action1() {
@Override
public void call(String s) {
System.out.println(s);
}
});
最后,用Java 8的lambdas表达式来去掉丑陋的Action1
代码。
Observable.just("Hello, world!")
.subscribe(s -> System.out.println(s));
如果你在Android中使用(迄今为止不能使用Java8)(译者注:原文作者写这篇文章的时候(2014年)Java8在Android中开发不能使用,在译者翻译这篇文章的时候(2016年),已经能使用部分特性了),我推荐使用retrolambda,它将大幅降低代码的啰嗦程度。
变换
让我们把事情变得更有趣。
假设我想要在输出的"Hello,world!"
语句中加上我的签名。一种可能(的实现方式)是改变Observable
:
Observable.just("Hello, world! -Dan")
.subscribe(s -> System.out.println(s));
如果你能够控制你的Observable
,这有效。但是不能保证以后都是这种情况。如果你使用的是别人的库呢?
另一种可能的问题是:如果我在多个地方使用我的Observable
,但仅仅是某些情况下想要加上签名呢?
那修改我们的Subscriber
怎样:
Observable.just("Hello, world!")
.subscribe(s -> System.out.println(s + " -Dan"));
这个回答同样不能让人满意,有不同的原因:我想要我的Subscriber
尽可能轻量,因为我可能会在主线程上运行它们。在更概念的层次上理解,Subscribers
被认定是做出反应(reacts)的事物,而不是做出转变(mutates)的事物。
如果我能够通过一些中间步骤将"Hello,world!"
转换,是不是很酷?
Operators介绍
接下来是如何解决item转换问题:使用operators。Operators被用于在源Observable
和最终的Subscriber
之间操作被发出的items。RxJava推出了非常多的operators,但是刚开始我们仅仅需要关注少数几个。
对于这种情况,map()
操作能被用于将一个被发出的item转化为另一个:
Observable.just("Hello, world!")
.map(new Func1() {
@Override
public String call(String s) {
return s + " -Dan";
}
})
.subscribe(s -> System.out.println(s));
同样的,我们能使用lambda来简化这个:
Observable.just("Hello, world!")
.map(s -> s + " -Dan")
.subscribe(s -> System.out.println(s));
非常酷,我们的map()
操作是一个转换一个item的Observable
。我们可以链式调用任意个map()
,将数据改进,成为最终的Subscriber
可消费的形式。
深入map()
map()
有一个有趣的方面:它不需要发出和源Observable
相同类型的items!
假设我的Subscriber
对输出原文本不感兴趣,想要输出原文本的hash码:
Observable.just("Hello, world!")
.map(new Func1() {
@Override
public Integer call(String s) {
return s.hashCode();
}
})
.subscribe(i -> System.out.println(Integer.toString(i)));
非常有趣——我们以String开始但是我们的Subscriber
接收的是一个Integer。
同样地,我们能使用lambda来简化代码:
Observable.just("Hello, world!")
.map(s -> s.hashCode())
.subscribe(i -> System.out.println(Integer.toString(i)));
就像我之前说的,我们想要Subscriber
尽可能少做事。通过另一个map()
来将hash码转化为String:
Observable.just("Hello, world!")
.map(s -> s.hashCode())
.map(i -> Integer.toString(i))
.subscribe(s -> System.out.println(s));
你有没有发现——Observable
和Subscriber
回到了它们之前的样子了!我们仅仅在它们之间增加了一些转换步骤。甚至能够添加我的签名:
Observable.just("Hello, world!")
.map(s -> s + " -Dan")
.map(s -> s.hashCode())
.map(i -> Integer.toString(i))
.subscribe(s -> System.out.println(s));
So What?
此刻你可能会想"对于一些简单的代码,用了很多花式步伐一样技巧"。对,那是简单的例子。但是有两点你需要掌握:
关键点1:Observable
和Subscriber
能做任何事情
Observale
可以是数据库查询,Subscriber
得到结果并将它们显示在屏幕上。Observable
可以是屏幕上的点击,Subscriber
对它做出反应。Observable
可以是从网络读取的字节流,Subscriber
把它写入磁盘。
RxJava是个能够处理任何问题的通用框架。
关键点2:Observable
和Subscriber
独立于在它们之间的转换步骤
我可以调用任意次的map
操作,在最初的源Observable
和它最终的Subscriber
之间。RxJava高度组件化:易于操作数据。只要操作于正确的输入输出数据,我可以制造一条无止尽的方法链。
综合以上两点,我们可以看到RxJava的巨大潜力。虽然此时我们仅仅有一个map()
操作,这严重地限制了我们的能力。在第二部分,我们将深入研究更多RxJava的操作。