Rxjava 2 vs Rxjava1 ,是否要升级?

文/wutongke(简书作者)
原文链接:http://www.jianshu.com/p/3ca96d96ffc0 

Rxjava 2 已经发布了,如果自己工程还在使用Rxjava 1,可以考虑升级到新版本。但是应该立马升级还是等等先忙手上其它的事情呢?

做决定之前,我们需要考虑一下投资回报率(Return on Investment),即花费在版本迁移上的时间是否值得。

升级的好处

Reactive Streams兼容性

Rxjava 2中一个结构性的变化是开始支持Reactive Streams, 为了达到这个目标,Rxjava进行了重新设计。

Reactive Streams提供了Reactive库运行的基本阐释和接口,我们大部分人不需要写Reactive库,但是Reactive Streams带来的好处可以使不同的reactive库协同工作。

比如类似Rxjava的Reactor 3 (目前Android工程师用不了,因为它只支持java8),两个库协同工作非常简单:

rxjava_reactor.gif

绿色部分是Rxjava2,红色部分是Reactor3

非常可惜的是我们不能使用Reactor3,其实Reactor3比Rxjava 2有10%~50%的性能提升。

BackPressure Observable/Flowable

在Rxjava 2中,有一个新的Reactive类型:Flowable,它与Observable很相似,但是有一个关键的不同是,Flowable支持backpressure.

这里解释一下什么是支持backpressure,有些人会问“支持backpressure就是不会再出现MissingBackpressureException了吗?”答案是:还是会出现。

支持backpressure的意思是如果事件的消费者不能及时消费生产的事件时可以定义一个处理事件的策略,开发者要自己实现这个事件处理策略。

Flowable

使用Flowables时,我们需要定义它的事件处理策略,有以下几种:

  • 缓存:当事件消费者不能及时处理事件时,产生的事件可以缓存起来,等待消费者消费

  • 丢弃:当事件消费者处理速度慢无法处理更多事件时,它可以丢弃所有的事件,当消费者可以继续工作时,处理最新生成的事件

  • 报错 事件消费者可以直接抛出MissingBackpressureException

事实上,很多开发者在实际的开发中并没有碰到过backpressure,所以这里写了一个demo读取加速度传感器并显示在屏幕上:

ezgif.com-resize.gif

Accelerometer using Flowables

Android设备上的加速度传感器每秒可以产生50次左右的数据,在屏幕上展示这些数据不会发生background,当然,这也会取决于reactive序列的处理负荷,但是也足以证明backpressure并不会常规事件(即不容易出现backpressure)。

Observable

Observable不支持backpressure, 意思是它永远不会抛出
MissingBackpressureException,当消费者不能及时消费事件时,事件会被缓存并等待被消费。

所有我们应该使用flowable还是Observable呢?

我认为当backpressure确实会发生并且不处理会导致异常时选择Flowable,在打印加速度传感器读数时我会选择flowable,因为如果花费时间读取而不仅仅是打印数据,backpressure肯定是会发生的。

如果事情并非如上所述,则可以使用Observable,比如用户多次点击几个按钮,我们可以缓存点击事件。

值得注意的是,如果使用Observable时缓存了大量的事件,可能会导致app crash。

我的经验是创建Observable时,考虑事件产生的形式:

  • 用户点击按钮事件   一秒钟最多只有几次,使用Observable

  • 感光或者加速度传感器    一秒钟产生几十次事件,使用Flowable
    记住如果处理事件非常耗时,就可能引起backpressure。

性能

Rxjava 2 的性能比Rxjava 1 要好(is better

使用高性能的库当然是好事,当程序的性能瓶颈是Rxjava 时尤其明显。你是否也看着代码吐槽 flatMap太慢了呢?

在android app中,计算性能通常不是问题,大多数时候,ui渲染才是瓶颈。

当计算调度上处理较多事情时并不会引起掉帧,而布局复杂,或者忘记在后台线程访问文件,或者在onDraw中创建bitmaps才会引起掉帧。

升级Rxjava 的挑战

没有nulls

近年来,越来越多的人吐槽nulls,这并不奇怪,连空指针的发明人都吐槽它是“10亿美元的错误”。

在Rxjava 1中我们可以使用null值,在新版本中,nulls完全不可用,如果在project中使用了null值,那就只能重写了。

可以选择Null Objects Pattern 或则 Optionals来代表 空值。

Dex Limit

我们知道,在Android开发中,要尽量避免方法数超过65000个。Rxjava 1中有多达5500个方法,而Rxjava 2中已经有超过9200个方法数。多出的4000个方法相比其带来的好处来讲也许是值得的,但是在逐步升级代码的过程中,项目中可能要保持两个版本的Rxjava,这将导致多达15000个方法,已经占到了Dex方法数的阈值的22%。

当然这些是没有使用proguard时候的统计 ,使用proguard之后,可能会减少上千方法数。

如果代码本身的数量已经超过了65000个,那再多加一些也许影响并不大,但是如果目前项目中方法数比较接近这个限制,就要好好考虑是否升级Rxjava是否值得了。

使用Operators

有时Rxjava中已有的Operators不能满足我们的需求,就需要自定义一些operators:

Now writing an operator specifically for 2.x is 10 times harder than for 1.x.
目前在Rxjava 2.x中写operator比在Rxjava 1.x中要难10倍

在Rxjava 1中定义operator不是一件轻松的事情,需要考虑到多线程的使用和backpressure的支持。

在Rxjava 2中更加复杂,首先,构造operator的方法改变了,之前我们只能通过臭名昭著的create方法,在Rxjava 2中,除了多线程使用,backpressure,cancellation和很多其它事情,我们可以考虑使用第四代特性(注:有人对reactive programming的发展过程进行了划分,可以参考http://akarnokd.blogspot.de/2016/03/operator-fusion-part-1.html ),
比如Operator Fusion 可以提升operator的性能,但同时也带来了一定的复杂性。

是否有必要自定义operators呢,我觉得除非自己定义的operator可以加入到Rxjava 2 或者其它reactive库中,不然我可以寻找是否有其它的解决办法。

首先,可以考虑组合已有的operator,使用 transformer是否可以解决问题,通常情况下这是可行的。组合已有operator的方式相比来讲比较简单。

如果执意要写一个operator,可以比较一下最简单的map操作服和比较复杂的flatMap,思考自己是否能胜任这个工作。

总结

总之,以上就是升级Rxjava 2 的一些利弊,你可以通过以上的分析思考一下升级是否是值得的。

目前来看,停留在Rxjava 1完全没有问题,因此Rxjava还在维护,如果有一天Rxjava 1 被废弃了,那就必须考虑升级了。

如果自己的项目还有持续超过一年的时间,那可以考虑升级了,如果不会超过一年,那停留在Rxjava 1可能更好一些。

如果对升级Rxjava比较感兴趣,可以持续关注文章。

此文为译文,原文地址The Next Step for Reactive Android Programming

来自:RxJava