在 第一篇blog 在中间,我介绍了RxJava的一些基础知识,也介绍了map()操作符。当然,如果你不想用RxJava,我一点也不惊讶。毕竟我接触过这么多。看完这个blog,相信你一定想马上在你的项目中使用RxJava。这个blog将介绍RxJava中的许多操作符。RxJava的强度来自于它定义的操作符。 先看一个例子:
准备工作假设我有这样的方法:
该方法根据输入字符串返回网站url列表(啊哈,搜索引擎)
[java] view plain copy
1. Observable<List<String>> query(String text);
现在我想建立一个强大的系统,它可以查询字符串并显示结果。根据上一个blog的内容,我们可以编写以下代码:
[java] view plain copy
1. query("Hello, world!") 2. .subscribe(urls -> { 3. for (String url : urls) { 4. System.out.println(url); 5. } 6. });
当然,这种代码是不能容忍的,因为上述代码使我们失去了改变数据流的能力。一旦我们想改变每个URL,我们只能在Subscriber中完成。我们没有使用这么酷的map()操作符!!!
当然,我可以使用map操作符。map的输入是urls列表,处理时仍需for each遍历,同样的蛋疼。
幸运的是,还有Observable.from()方法,它接收集合作为输入,然后每次向subscriber输出一个元素:
[java] view plain copy
1. Observable.from(url1), “url2”, “url3” 2. .subscribe(url -> System.out.println(url));
让我们在刚才的场景中使用这种方法:
[java] view plain copy
1. query("Hello, world!") 2. .subscribe(urls -> { 3. Observable.from(urls) 4. .subscribe(url -> System.out.println(url)); 5. });
尽管去掉了for each循环,但代码看起来仍然很混乱。多嵌套subscription不仅看起来丑陋,而且难以修改,更严重的是,它会破坏RxJava的一些特性,我们还没有提到。
改进救星来了,他是flatMap()。
Observable.flatMap()接收Observable的输出作为输入,同时输出另一个Observable。直接查看代码:
[java] view plain copy
1. query("Hello, world!") 2. new Func1<List<String>, Observable<String>>() { 3. @Override 4. public Observable<String> call(List<String> urls) { 5. return Observable.from(urls); 6. } 7. }) 8. .subscribe(url -> System.out.println(url));
为了方便您了解发生了什么,我在这里贴出了整个函数代码,使用lambda可以大大简化代码长度:
[java] view plain copy
1. query("Hello, world!") 2. .flatMap(urls -> Observable.from(urls)) 3. .subscribe(url -> System.out.println(url));
flatMap()看起来奇怪吗?为什么要回到另一个observable?理解flatmap的关键在于,flatmap输出的新observable正是我们想在subscriber中接收的。现在Subscriber不再收到Listtter<String>,相反,收到一些单个字符串,就像Observable一样.from()输出相同。
这部分也是我学RxJava时最难理解的部分。一旦突然意识到,RxJava的很多问题都会一起解决。
还可以更好flatMap()真的不能更好,它可以返回任何它想要返回的Observable对象。
例如,以下方法:
[java] view plain copy
1. // 回到网站的标题,如果404,回到nulll 2. Observable<String> getTitle(String URL);
然后,在之前的例子中,我现在不想打印URL,而是想打印我收到的每个网站的标题。问题是,我的方法每次只能输入一个URL,返回值不是String,而是输出String的Observabl对象。使用flatMapp可以简单地解决这个问题。
[java] view plain copy
1. query("Hello, world!") 2. .flatMap(urls -> Observable.from(urls)) 3. new Func1<String, Observable<String>>() { 4. @Override 5. public Observable<String> call(String url) { 6. return getTitle(url); 7. } 8. }) 9. .subscribe(title -> System.out.println(title));
使用lambda:
[java] view plain copy
1. query("Hello, world!") 2. .flatMap(urls -> Observable.from(urls)) 3. .flatMap(url -> getTitle(url)) 4. .subscribe(title -> System.out.println(title));
是不是觉得不可思议?我可以把多种独立返回Observable对象的方法结合起来!帅!
不仅如此,我还将两个API的调用组合到一个链式调用中。我们可以调用任何多个API链接。每个人都应该知道同步所有API调用,然后将所有API调用的回调结果组合成需要显示的数据是多么痛苦。在这里,我们成功地避免了callback hell(多层嵌套的回调使代码难以阅读和维护)。现在所有的逻辑都被包装成这个简单的响应调用。
操作符丰富到目前为止,我们已经接触到两个操作符,RxJava中有更多的操作符,那么我们如何使用其他操作符来改进我们的代码呢?
getTitle()如果url不存在,则返回null。我们不想输出“null“,然后我们可以从返回的title列表中过滤null值!
[java] view plain copy
1. query("Hello, world!") 2. .flatMap(urls -> Observable.from(urls)) 3. .flatMap(url -> getTitle(url)) 4. null) 5. .subscribe(title -> System.out.println(title));
filter()输出和输入相同的元素,并过滤掉那些不是的元素符合检查条件的。
假如我们最多只想要5个结果:
[java] view plain copy
1. query("Hello, world!") 2. .flatMap(urls -> Observable.from(urls)) 3. .flatMap(url -> getTitle(url)) 4. null) 5. 5) 6. .subscribe(title -> System.out.println(title));
take()输出最多指定数量的结果。
如果我们想在打印前将每个标题保存到磁盘中:
[java] view plain copy
1. query("Hello, world!") 2. .flatMap(urls -> Observable.from(urls)) 3. .flatMap(url -> getTitle(url)) 4. null) 5. 5) 6. .doOnNext(title -> saveTitle(title)) 7. .subscribe(title -> System.out.println(title));
doOnNext()允许我们在每次输出元素之前做一些额外的事情,比如这里的保存标题。
看看这里操作数据流有多简单。您可以添加任何更多的操作,而不会扰乱您的代码。
RxJava包含大量的操作符。操作符的数量有点吓人,但值得一个接一个地看,这样你就可以知道哪些操作符可以使用。理解这些操作符可能需要一些时间,但一旦你理解了它们,你就完全掌握了RxJava的力量。
你甚至可以编写自定义操作符!这个blog不打算定制自定义操作符。如果你想,清楚自己的谷歌。
感觉如何?嗯,你是个怀疑主义者,而且很难被说服,那你为什么要关心这些操作符呢?
因为操作符可以让你操作任何数据流。
链接一系列操作符可以完成复杂的逻辑。代码被分解成一系列可组合的片段。这就是响应函数编程的魅力所在。你使用的越多,你的编程思维就越多。
此外,RxJava还使我们更容易处理数据。在最后一个例子中,我们调用两个API来处理API返回的数据,然后保存到磁盘中。但我们的Subscriber并不知道这一点,它只是认为它正在接收一个Observable<String>对象。良好的包装也给编码带来了便利!
在第三部分,我将介绍RxJava的其他酷特性,如错误处理和并发,这些特性不会直接用于处理数据。