彻底了解RxJava(一)基础知识

彻底了解RxJava(一)基础知识

——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢——

原文链接:http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

本文Gitbooks链接:http://asce1885.gitbooks.io/android-rd-senior-advanced/content/che_di_le_jie_rxjava_ff08_yi_ff09_ji_chu_zhi_shi.html

RxJava是目前在Android开发者中新兴热门的函数库。唯一的问题是刚开始接触时会感到较难理解。函数响应式编程对于“外面世界”来的开发人员而言是很难理解的,但一旦理解了它,你会感觉真是太棒了。

我将介绍RxJava的一些知识,这系列文章(四部分)的目标是把你领进RxJava的大门。我不会解释所有相关的知识点(我也做不到),我只想引起你对RxJava的兴趣并知道它是如何工作的。

基础知识

响应式代码的基本组成部分是Observables和Subscribers(事实上Observer才是最小的构建块,但实践中使用最多的是Subscriber,因为Subscriber才是和Observables的对应的。)。Observable发送消息,而Subscriber则用于消费消息。

消息的发送是有固定模式的。Observable可以发送任意数量的消息(包括空消息),当消息被成功处理或者出错时,流程结束。Observable会调用它的每个Subscriber的Subscriber.onNext()函数,并最终以Subscriber.onComplete()或者Subscriber.onError()结束。

这看起来像标准的观察者模式,但不同的一个关键点是:Observables一般只有等到有Subscriber订阅它,才会开始发送消息(术语上讲就是热启动Observable和冷启动Observable。热启动Observable任何时候都会发送消息,即使没有任何观察者监听它。冷启动Observable只有在至少有一个订阅者的时候才会发送消息(我的例子中都是只有一个订阅者)。这个区别对于开始学习RxJava来说并不重要。)。换句话说,如果没有订阅者观察它,那么将不会起什么作用。

Hello, World!

让我们以一个具体例子来实际看看这个框架。首先,我们创建一个基本的Observable:

Observable<String> myObservable = Observable.create(
    new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> sub) {
            sub.onNext("Hello, world!");
            sub.onCompleted();
        }
    }
);

我们的Observable发送“Hello,world!”消息然后完成。现在让我们创建Subscriber来消费这个数据:

Subscriber<String> mySubscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) { System.out.println(s); }

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }
};

上面代码所做的工作就是打印由Observable发送的字符串。现在我们有了myObservable和mySubscriber,就可以通过subscribe()函数把两者关联起来:

myObservable.subscribe(mySubscriber);
// Outputs "Hello, world!"

当订阅完成,myObservable将调用subscriber的onNext()和onComplete()函数,最终mySubscriber打印“Hello, world!”然后终止。

更简洁的代码

上面为了打印“Hello, world!”写了大量的样板代码,目的是为了让你详细了解到底发生了什么。RxJava提供了很多快捷方式来使编码更简单。

首先让我们简化Observable,RxJava为常见任务提供了很多内建的Observable创建函数。在以下这个例子中,Observable.just()发送一个消息然后完成,功能类似上面的代码(严格来说,Observable.just()函数跟我们原来的代码并不完全一致,但在本系列第三部分之前我不会说明原因):

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

接下来,让我们处理Subscriber不必要的样板代码。如果我们不关心onCompleted()或者onError()的话,那么可以使用一个更简单的类来定义onNext()期间要完成什么功能:

Action1<String> onNextAction = new Action1<String>() {
    @Override
    public void call(String s) {
        System.out.println(s);
    }
};

Actions可以定义Subscriber的每一个部分,Observable.subscribe()函数能够处理一个,两个或者三个Action参数,分别表示onNext(),onError()和onComplete()函数。上面的Subscriber现在如下所示:

myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);

然而,现在我们不需要onError()和onComplete()函数,因此只需要第一个参数:

myObservable.subscribe(onNextAction);
// Outputs "Hello, world!"

现在,让我们把上面的函数调用链接起来从而去掉临时变量:

Observable.just("Hello, world!")
    .subscribe(new Action1<String>() {
        @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平台上(因此不能使用Java 8),那么我严重推荐使用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"));

这个解决方案也不尽如人意,不过原因不同于上面:我想Subscribers尽可能的轻量级,因为我可能要在主线程中运行它。从更概念化的层面上讲,Subscribers是用于被动响应的,而不是主动发送消息使其他对象发生变化。

如果可以通过某些中间步骤来对上面的“Hello, world!”进行转换,那岂不是很酷?

Operators简介

接下来我们将介绍如何解决消息转换的难题:使用Operators。Operators在消息发送者Observable和消息消费者Subscriber之间起到操纵消息的作用。RxJava拥有大量的opetators,但刚开始最好还是从一小部分开始熟悉。

这种情况下,map() operator可以被用于将已被发送的消息转换成另外一种形式:

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

同样的,我们可以使用Java 8的lambdas表达式来简化代码:

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

很酷吧?我们的map() operator本质上是一个用于转换消息对象的Observable。我们可以级联调用任意多个的map()函数,一层一层地将初始消息转换成Subscriber需要的数据形式。

map()更多的解释

map()函数有趣的一点是:它不需要发送和原始的Observable一样的数据类型。假如我的Subscriber不想直接输出原始的字符串,而是想输出原始字符串的hash值:

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

有趣的是,我们的原始输入是字符串,但Subscriber最终收到的是Integer类型。同样的,我们可以使用lambdas简化代码如下:

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

正如我前面说过的,我希望Subscriber做尽量少的工作,我们可以把hash值转换成字符串的操作移动到一个新的map()函数中:

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));

那又怎样?

到这里你可能会想“为了得到简单的代码有很多聪明的花招”。确实,这是一个简单的例子,但有两个概念你需要理解:

关键概念#1:Observable和Subscriber能完成任何事情。

放飞你的想象,任何事情都是可能的。

你的Observable可以是一个数据库查询,Subscriber获得查询结果然后将其显示在屏幕上。你的Observable可以是屏幕上的一个点击,Subscriber响应该事件。你的Observable可以从网络上读取一个字节流,Subscriber将其写入本地磁盘中。

这是一个可以处理任何事情的通用框架。

关键概念#2:Observable和Subscriber与它们之间的一系列转换步骤是相互独立的。

我们可以在消息发送者Observable和消息消费者Subscriber之间加入任意多个想要的map()函数。这个系统是高度可组合的:它很容易对数据进行操纵。只要operators符合输入输出的数据类型,那么我可以得到一个无穷尽的调用链(好吧,并不是无穷尽的,因为总会到达物理机器的极限的,但你知道我想表达的意思)。

结合两个关键概念,你可以看到一个有极大潜能的系统。然而到这里我们只介绍了一个operator:map(),这严重地限制了我们的可能性。在本系列的第二部分,我们将详细介绍RxJava中大量可用的operators。

——欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢——

时间: 2024-10-21 05:28:38

彻底了解RxJava(一)基础知识的相关文章

MySQL数据库基础知识

day02 MySQL数据库基础知识 一.基础知识概述: 基础决定你这门课程的学习成败!只有学习好这些基础知识以后,你才能真正的运用自如.才能够对数据库有更深入的了解,道路才会越走越远. 二.基础知识: 1.数据库(database):数据库就好比是一个物理的文档柜,一个容器,把我们整理好的数据表等等归纳起来. 创建数据库命令:        create database 数据库名; 2.查看数据库         show databases; 3.打开指定的数据库         use 

linux入门基础知识及简单命令介绍

linux入门基础知识介绍 1.计算机硬件组成介绍 计算机主要由cpu(运算器.控制器),内存,I/O,外部存储等构成. cpu主要是用来对二进制数据进行运算操作,它从内存中取出数据,然后进行相应的运算操作.不能从硬盘中直接取数据. 内存从外部存储中取出数据供cpu运存.内存的最小单位是字节(byte) 备注:由于32的cpu逻辑寻址能力最大为32内存单元.因此32位cpu可以访问的最大内存空间为:4GB,算法如下: 2^32=2^10*2^10*2^10*2^2 =1024*1024*1024

BroadcastReceive基础知识总结

BroadcastReceive基础知识总结 1.BroadcastReceive简介 BroadcastReceive也就是"广播接收者"的意思,顾名思义,就是用来接收来自系统和应用中的广播 在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能,当网络状态改变时,系统会产生一条广播,接收到这条广播,就能及时的做出提示和保存数据等操作,当电池的电量改变的时候,系统会产生一条广播,接收到这条广播就能在电量低的时候告知用户

基础知识--:before伪元素和:after伪元素

http://book.51cto.com/art/201108/285688.htm 3.7  替换指定位置 大家都知道before和after是前.后的意思.但是奇怪的是,CSS中的:before伪元素和:after伪元素是为源文档中不存在的内容设置样式的. 没有内容怎么设置样式呢?别急!它们有一个content属性,一起使用就可以为某个选择器前.后的内容设置样式了. 下面就来了解一下:before伪元素和:after伪元素的用法. 视频教学:光盘/视频/3/3.7  替换指定位置.avi 

20_Shell语言———VIM编辑器基础知识三之窗口属性定制、配置文件及查找替换功能

Vim编辑器可以让用户按照需求来定制一些使用属性. 一.窗口属性定义 1)显示行号 行号不是内容,只是用来帮助用户确认文本所在的行.在vim编辑器中,如果要显示行号,可以在末行模式下输入: set number 如果想关闭,则可以在功能名称前面加上no,即: set nonumber 命令可以被简写,如set number 可以简写为 set nu:set nonumber 可以简写为 set nonu. 注意,上述设定仅对当前vim的进程有效,一旦当前进程关闭,这些设定就会失效,如果要使设定永

web基础知识(一)关于ajax传值最基础东西

HTTP方法之 GET对比POST GET:从指定的资源请求数据, POST:向指定的资源提交要被处理的数据 GET方法: 请注意,查询字符串(名称/值对)是在 GET 请求的 URL 中发送的: /test/demo_form.asp?name1=value1&name2=value2 有关 GET 请求的其他一些注释: GET 请求可被缓存 GET 请求保留在浏览器历史记录中 GET 请求可被收藏为书签 GET 请求不应在处理敏感数据时使用 GET 请求有长度限制 GET 请求只应当用于取回

线程基础知识

什么是线程: 在一个程序里的一个执行路线就叫做线程(thread).更准确的定义是:线程是"一个进程内部的控制序列" 一切进程至少都有一个执行线程 进程与线程 进程是资源竞争的基本单位 线程是程序执行的最小单位 线程共享进程数据,但也拥有自己的一部分数据 线程ID 一组寄存器 栈 errno 信号状态 优先级 fork和创建新线程的区别 当一个进程执行一个fork调用的时候,会创建出进程的一个新拷贝,新进程将拥有它自己的变量和它自己的PID.这个新进程的运行时间是独立的,它在执行时几乎

Keepalived基础知识

大纲: 一.什么是Keepalived? 二.VRRP协议简介. 三.Keepalived原理. 四.Keepalived配置文件详解. 五.Keepalived配置示例. 一.什么是Keepalived? 什么是Keepalived呢,keepalived观其名可知,保持存活,在网络里面就是保持在线了,也就是所谓的高可用或热备,用来防止单点故障(单点故障是指一旦某一点出现故障就会导致整个系统架构的不可用)的发生,那说到keepalived时不得不说的一个协议就是VRRP协议,可以说这个协议就是

【Python数据挖掘课程】六.Numpy、Pandas和Matplotlib包基础知识

前面几篇文章采用的案例的方法进行介绍的,这篇文章主要介绍Python常用的扩展包,同时结合数据挖掘相关知识介绍该包具体的用法,主要介绍Numpy.Pandas和Matplotlib三个包.目录:        一.Python常用扩展包        二.Numpy科学计算包        三.Pandas数据分析包        四.Matplotlib绘图包 前文推荐:       [Python数据挖掘课程]一.安装Python及爬虫入门介绍       [Python数据挖掘课程]二.K