理解RxJava:(一)基础知识

本文翻译自Grokking RxJava, Part 1: The Basics,著作权归原作者danlew所有。译文由JohnTsai翻译。转载请注明出处,并保留此段声明。

RxJava这些天成为了Android开发者关注的新热点。唯一的问题是它在你刚接触时难以理解。当你习惯了命令式编程,函数响应式编程就变得难以理解。但是一旦你理解了它,它就变得很棒了。

我在这试着给你们带来不一样的RxJava。这一系列四篇文章的目标是带你们入门。我不会也不能讲解所有的东西。我只是想让你们对RxJava以及它的工作原理感兴趣。

基础知识

响应式代码的基本构成部分是ObservablesSubscribers(译者注:技术名词很难找到合适的中文翻译,所以维持原文不被翻译)。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:

  1. Observable<String> myObservable = Observable.create(
  2. new Observable.OnSubscribe<String>() {
  3. @Override
  4. public void call(Subscriber<? super String> sub) {
  5. sub.onNext("Hello, world!");
  6. sub.onCompleted();
  7. }
  8. }
  9. );

Observable发出Hello World然后完成。现在创建一个Subscriber来消费掉数据。

  1. Subscriber<String> mySubscriber = new Subscriber<String>() {
  2. @Override
  3. public void onNext(String s) { System.out.println(s); }
  4. @Override
  5. public void onCompleted() { }
  6. @Override
  7. public void onError(Throwable e) { }
  8. };

所有这些所做的是将Observable发出的每个String打印出来。

现在有了myObservablemySubscriber,我们可以用subscribe()方法将它们连接起来。

  1. myObservable.subscribe(mySubscriber);
  2. // 输出 "Hello, world!"

当订阅发生时,myobservable调用subsriberonNext()onComplete()方法。作为结果,mySubscriber输出"Hello,World"然后结束。

更简洁的代码

为了输出"Hello,World!",上面写了许多样板代码。这是因为我为了让你们能够明确发生了什么,选择了一种啰嗦的方式。RxJava提供了许多快捷写法让我们能写出更简洁的代码。

首先,简化Observable。RxJava有针对通用任务的多种内置Observable构建方法。在这种情况下,Observable.just()发出一个item然后完成结束,就像我们上面的代码:

  1. Observable<String> myObservable =
  2. Observable.just("Hello, world!");

然后,对于啰嗦的Subscriber。我们不关心onCompleted()onError()方法,取而代之,我们可以用一个更简洁的类来定义在onNext()中做什么:

  1. Action1<String> onNextAction = new Action1<String>() {
  2. @Override
  3. public void call(String s) {
  4. System.out.println(s);
  5. }
  6. };

Subscriber每部分的Action都能自定义。Observable.subscribe()能处理一个,两个以及三个Action参数,以取代onNext(),onError()onComplete()方法。复用我们之前的Subscriber,如下:

  1. myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);

然而,我们仅仅需要第一个参数,因为我们可以省略onError()onComplete()方法:

  1. myObservable.subscribe(onNextAction);
  2. // 输出 "Hello, world!"

现在,让我们通过方法的链式调用来取代这些变量:

  1. Observable.just("Hello, world!")
  2. .subscribe(new Action1<String>() {
  3. @Override
  4. public void call(String s) {
  5. System.out.println(s);
  6. }
  7. });

最后,用Java 8的lambdas表达式来去掉丑陋的Action1代码。

  1. Observable.just("Hello, world!")
  2. .subscribe(s -> System.out.println(s));

如果你在Android中使用(迄今为止不能使用Java8)(译者注:原文作者写这篇文章的时候(2014年)Java8在Android中开发不能使用,在译者翻译这篇文章的时候(2016年),已经能使用部分特性了),我推荐使用retrolambda,它将大幅降低代码的啰嗦程度。

变换

让我们把事情变得更有趣。

假设我想要在输出的"Hello,world!"语句中加上我的签名。一种可能(的实现方式)是改变Observable:

  1. Observable.just("Hello, world! -Dan")
  2. .subscribe(s -> System.out.println(s));

如果你能够控制你的Observable,这有效。但是不能保证以后都是这种情况。如果你使用的是别人的库呢?

另一种可能的问题是:如果我在多个地方使用我的Observable,但仅仅是某些情况下想要加上签名呢?

那修改我们的Subscriber怎样:

  1. Observable.just("Hello, world!")
  2. .subscribe(s -> System.out.println(s + " -Dan"));

这个回答同样不能让人满意,有不同的原因:我想要我的Subscriber尽可能轻量,因为我可能会在主线程上运行它们。在更概念的层次上理解,Subscribers被认定是做出反应(reacts)的事物,而不是做出转变(mutates)的事物。

如果我能够通过一些中间步骤将"Hello,world!"转换,是不是很酷?

Operators介绍

接下来是如何解决item转换问题:使用operators。Operators被用于在源Observable和最终的Subscriber之间操作被发出的items。RxJava推出了非常多的operators,但是刚开始我们仅仅需要关注少数几个。

对于这种情况,map()操作能被用于将一个被发出的item转化为另一个:

  1. Observable.just("Hello, world!")
  2. .map(new Func1<String, String>() {
  3. @Override
  4. public String call(String s) {
  5. return s + " -Dan";
  6. }
  7. })
  8. .subscribe(s -> System.out.println(s));

同样的,我们能使用lambda来简化这个:

  1. Observable.just("Hello, world!")
  2. .map(s -> s + " -Dan")
  3. .subscribe(s -> System.out.println(s));

非常酷,我们的map()操作是一个转换一个item的Observable。我们可以链式调用任意个map()

,将数据改进,成为最终的Subscriber可消费的形式。

深入map()

map()有一个有趣的方面:它不需要发出和源Observable相同类型的items!

假设我的Subscriber对输出原文本不感兴趣,想要输出原文本的hash码:

  1. Observable.just("Hello, world!")
  2. .map(new Func1<String, Integer>() {
  3. @Override
  4. public Integer call(String s) {
  5. return s.hashCode();
  6. }
  7. })
  8. .subscribe(i -> System.out.println(Integer.toString(i)));

非常有趣——我们以String开始但是我们的Subscriber接收的是一个Integer。

同样地,我们能使用lambda来简化代码:

  1. Observable.just("Hello, world!")
  2. .map(s -> s.hashCode())
  3. .subscribe(i -> System.out.println(Integer.toString(i)));

就像我之前说的,我们想要Subscriber尽可能少做事。通过另一个map()来将hash码转化为String:

  1. Observable.just("Hello, world!")
  2. .map(s -> s.hashCode())
  3. .map(i -> Integer.toString(i))
  4. .subscribe(s -> System.out.println(s));

你有没有发现——ObservableSubscriber回到了它们之前的样子了!我们仅仅在它们之间增加了一些转换步骤。甚至能够添加我的签名:

  1. Observable.just("Hello, world!")
  2. .map(s -> s + " -Dan")
  3. .map(s -> s.hashCode())
  4. .map(i -> Integer.toString(i))
  5. .subscribe(s -> System.out.println(s));

So What?

此刻你可能会想"对于一些简单的代码,用了很多花式步伐一样技巧"。对,那是简单的例子。但是有两点你需要掌握:

关键点1:ObservableSubscriber能做任何事情

Observale可以是数据库查询,Subscriber得到结果并将它们显示在屏幕上。Observable可以是屏幕上的点击,Subscriber对它做出反应。Observable可以是从网络读取的字节流,Subscriber把它写入磁盘。

RxJava是个能够处理任何问题的通用框架。

关键点2:ObservableSubscriber独立于在它们之间的转换步骤

我可以调用任意次的map操作,在最初的源Observable和它最终的Subscriber之间。RxJava高度组件化:易于操作数据。只要操作于正确的输入输出数据,我可以制造一条无止尽的方法链。

综合以上两点,我们可以看到RxJava的巨大潜力。虽然此时我们仅仅有一个map()操作,这严重地限制了我们的能力。在第二部分,我们将深入研究更多RxJava的操作。

理解RxJava:(一)基础知识的更多相关文章

  1. [C# 基础知识系列]专题九: 深入理解泛型可变性

    引言: 在C# 2.0中泛型并不支持可变性的(可变性指的就是协变性和逆变性),我们知道在面向对象的继承中就具有可变性,当方法声明返回类型为Stream,我们可以在实现中返回一个FileStream的类 ...

  2. 深入理解mysql之BDB系列(1)---BDB相关基础知识

        深入理解mysql之BDB系列(1) ---BDB相关基础知识 作者:杨万富   一:BDB体系结构 1.1.BDB体系结构 BDB总体的体系结构如图1.1所看到的,包括五个子系统(见图1.1 ...

  3. IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理

    1.前言 一个安全的信息系统,合法身份检查是必须环节.尤其IM这种以“人”为中心的社交体系,身份认证更是必不可少. 一些PC时代小型IM系统中,身份认证可能直接做到长连接中(也就是整个IM系统都是以长 ...

  4. IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列

    1.引言 消息是互联网信息的一种表现形式,是人利用计算机进行信息传递的有效载体,比如即时通讯网坛友最熟悉的即时通讯消息就是其具体的表现形式之一. 消息从发送者到接收者的典型传递方式有两种: 1)一种我 ...

  5. 爬虫基础---HTTP协议理解、网页的基础知识、爬虫的基本原理

    一.HTTP协议的理解 URL和URI 在学习HTTP之前我们需要了解一下URL.URI(精确的说明某资源的位置以及如果去访问它) URL:Universal Resource Locator 统一资 ...

  6. JMeter性能测试的基础知识和个人理解

    JMeter性能测试的基础知识和个人理解 1. JMeter的简介   JMeter是Apache组织开发的开源项目,设计之初是用于做性能测试的,同时它在实现对各种接口的调用方面做的比较成熟,因此,常 ...

  7. IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

    本文引用了简书作者“骑小猪看流星”技术文章“Cookie.Session.Token那点事儿”的部分内容,感谢原作者. 1.前言 众所周之,IM是个典型的快速数据流交换系统,当今主流IM系统(尤其移动 ...

  8. C#基础知识1-深入理解值类型和引用类型

    C#值类型和引用类型这个概念在刚学习的时候应该就知道了.但是我们并没有深入的去理解它.越是基础知识其实才是最有用的.对代码的优化,代码质量的提升都有帮助.通过整理本文章,对很多知识也起到了巩固的作用吧 ...

  9. python基础知识的学习和理解

    参考链接:https://github.com/yanhualei/about_python/tree/master/python_learning/python_base   python基础知识笔 ...

随机推荐

  1. 最新 Windows 10 应用项目模板发布

    以下是最新的Visual Studio 2015 Windows 10 应用程序模板. Windows 10中几乎所有的官方应用都遵循这样一个设计模板:在左上方有一个所谓的导航栏.点击该导航按钮,左侧 ...

  2. 支持事件穿透?使用pointer-events样式

    使用绝对定位元素,让元素A完全盖住元素B时,如何通过元素A来响应元素B的事件呢? 上图可以用下面的SVG代码来实现: <svg width="200" height=&quo ...

  3. javascript基础知识复习一

    JavaScript 一.数据类型 A.String B.Number C.boolean  1.undefined.false.null.0.“”这五个返回的都是false: 2.NAN==NAN返 ...

  4. JavaWeb学习总结(十七)——JSP中的九个内置对象

    一.JSP运行原理 每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理.JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet ...

  5. Leetcode 110 Balanced Binary Tree 二叉树

    判断一棵树是否是平衡树,即左右子树的深度相差不超过1. 我们可以回顾下depth函数其实是Leetcode 104 Maximum Depth of Binary Tree 二叉树 /** * Def ...

  6. 关闭Windows Update更新驱动程序

    关于Win10的更新配置,特别是自动更新驱动程序,经常会导致驱动安装错误而无法开机的问题. 此时只好开机时按F8进入高级模式恢复最后一次正确配置,或者在安全模式删除错误的驱动程序. 关于Win10的更 ...

  7. 解决.Net 4.0 A potentially dangerous Request.Form value was detected from the client 异常

    在web.config中加入 <httpRuntime maxRequestLength="22000" executionTimeout="43200" ...

  8. Unity3D Shader入门指南(一)

    动机 自己使用Unity3D也有一段时间了,但是很多时候是流于表面,更多地是把这个引擎简单地用作脚本控制,而对更深入一些的层次几乎没有了解.虽然说Unity引擎设计的初衷就是创建简单的不需要开发者操心 ...

  9. Jmeter调试工具---HTTP Mirror Server

    之前我介绍过Jmeter的一种调试工具Debug Sampler,它可以输出Jmeter的变量.属性甚至是系统属性而不用发送真实的请求到服务器.既然这样,那么HTTP Mirror Server又是做 ...

  10. 2014 Hangjs 见闻流水账第一天

    前言 6月21日~6月22日, 第一次跑远门去参加一个大会(广州 -> 杭州),本来打算,在火车的回来的路上,把这两天的东西记录一下,不过,火车上的环境实在恶劣,同时也高估了自己的专注力,所以, ...