浅谈RxJava与2.0的新特性
简介
说起 RxJava ,相信诸多 Android 开发者都不会陌生。作为一个知名的响应式编程库,从前年开始逐渐变得火热,从小众到被众多 Android 开发者们广泛引入与流传,其在 GitHub 的 仓库 截止笔者写这篇文章时,已经有16400+个 star 。甚至有一些大牛专门为 Android 写了 RxJava 的适配库,如
- RxAndroid
- RxBinding
- RxLifecycle
为什么 RxJava 如此受到 Android 开发者们的欢迎。我想不外乎两个原因。 1. 异步 2. 链式操作
异步
对 Android 线程有所了解的朋友都知道, Android的 UI 绘制 与 事件响应是在主线程的,为了保证界面的流畅性,很多耗时操作如读写数据库、读写文件、请求网络,我们都会挪到异步线程去完成,再回调到主线程。当然在4.0以后主线程直接就不允许请求网络了。
在过去没有 RxJava 的时候,开发者一般都是通过 AsyncTask , Thread ,更好些的就是通过线程池来完成这些任务。而有了 RxJava 以后,简简单单的一句话就可以随意的切换线程,简直不用太舒服。
最典型的 RxJava 中的 Observable
类,提供了2个函数, 分别是 subscribeOn
与observeOn
。前者可以切换被观察时的线程(如果说数据发射的线程不够严谨,数据并非一定在观察时发射的,尤其是开发者自定义 OnSubscribe
时),后者可以切换数据被消费时的线程。
举一个切换线程的例子:
Log.i("debug", Thread.currentThread().getName()); Observable.empty() .doOnCompleted(new Action0() {@Overridepublic void call() {Log.i("debug", Thread.currentThread().getName());}}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).doOnCompleted(new Action0() {@Overridepublic void call() {Log.i("debug", Thread.currentThread().getName());}}).subscribe();
这里我们没有任何数据,就仅仅发射了一个 onComplete
,但是在切换线程的代码中,我们增加了 onComplte
时要额外执行的代码,输出结果如下:
08-27 10:47:41.173 6741-6741/com.dieyidezui.rxjavademo I/debug: main 08-27 10:47:41.201 6741-6762/com.dieyidezui.rxjavademo I/debug: RxIoScheduler-2 08-27 10:47:41.217 6741-6741/com.dieyidezui.rxjavademo I/debug: main
这仅仅是简单的例子, RxJava 提供了很多便捷的操作符供我们使用,如 map
、 filter
、 flatMap
、 merge
、 concat
等。可见当熟练使用后对我们的编程效率确实有很大帮助。尤其是 MVP 模式, RxJava 与之结合可谓是”天作之合”。
链式操作
上面笔者演示的代码其实就是 RxJava 的典型使用方式:
- 发射数据源
- 中间操作
- 处理结果
其中中间操作包含诸多用法, 如果切换线程,变换数据等。
为什么我说链式操作很好。第一,链式逻辑替代深度回调逻辑,容易编写,不易出 BUG 。第二,RxJava 提供诸多了整体处理数据的操作符,非常实用。第三,配合 Java8 的 lambda 表达式,使代码简短优雅。
好了,对 RxJava 的介绍就此为止了。进阶用法、原理剖析以后会有专门的文章。对 RxJava 不熟悉的同学,建议先去看一下官方的 wiki 。链接: https://github.com/ReactiveX/RxJava/wiki
RxJava2.0
前天, RxJava终于发布了2.0 RC1 版本,一直关注于此的笔者立刻就进去尝鲜了。结合官方的介绍,笔者总结并翻译了一些与 1.x 的异同与大家分享。
包名与MAVEN依赖
首先要说的就是 RxJava 2和1是 互相独立 的。因此包名与 maven 的依赖也是不一样的,就类似于 OkHttp 3与2一样。 RxJava 2.x的依赖是全新的 io.reactivex.rxjava2:rxjava:2.x.y
,并且类处于该 io.reactivex
包名下,而不再是 rx
。
接口变化
RxJava2 是遵循 Reactive Streams Specification 的规范完成的,新的特性依赖其提供的4个基础接口。分别是
- Publisher
- Subscriber
- Subscription
- Processor
Flowable与Observable
新的实现叫做 Flowable
, 同时旧的 Observable
也保留了。因为在 RxJava1.x 中,有很多事件不被能正确的背压,从而抛出 MissingBackpressureException
。
举个简单的例子,在 RxJava1.x 中的 observeOn
, 因为是切换了消费者的线程,因此内部实现用队列存储事件。在 Android 中默认的 buffersize 大小是16,因此当消费比生产慢时, 队列中的数目积累到超过16个,就会抛出 MissingBackpressureException
, 初学者很难明白为什么会这样,使得学习曲线异常得陡峭。
而在 2.0 中,Observable 不再支持背压,而Flowable 支持非阻塞式的背压。并且规范要求,所有的操作符强制支持背压。幸运的是, Flowable 中的操作符大多与旧有的 Observable 类似。
Single、Completable
Single 与 Completable 都基于新的 Reactive Streams 的思想重新设计了接口,主要是消费者的接口, 现在他们是这样的:
interface SingleObserver<T> { void onSubscribe(Disposable d);void onSuccess(T value);void onError(Throwable error); }interface CompletableObserver<T> { void onSubscribe(Disposable d);void onComplete();void onError(Throwable error); }
Subscriber
对比一下 Subscriber :
public interface Subscriber<T> { public void onSubscribe(Subscription s);public void onNext(T t);public void onError(Throwable t);public void onComplete(); }
我们会发现和以前不一样的是多了一个 onSubscribe
的方法, Subscription
如下:
Subscription
public interface Subscription { public void request(long n);public void cancel(); }
熟悉 RxJava 1.x 的朋友能发现, 新的 Subscription
更像是综合了旧的 Producer
与 Subscription
的综合体。他既可以向上游请求数据,又可以打断并释放资源。而旧的 Subscription
在这里因为名字被占,而被重新命名成了 Disposable
Disposable
public interface Disposable { void dispose();boolean isDisposed(); }
这里最大的不同就是这个 onSubscribe
,根据 Specification, 这个函数一定是第一个被调用的, 然后就会传给调用方一个 Subscription
,通过这种方式组织新的背压关系。当我们消费数据时,可以通过 Subscription
对象,自己决定请求数据。
这里就可以解释上面的非阻塞的背压。旧的阻塞式的背压,就是根据下游的消费速度,中游可以选择阻塞住等待下游的消费,随后向上游请求数据。而新的非阻塞就不在有中间阻塞的过程,由下游自己决定取多少,还有背压策略,如抛弃最新、抛弃最旧、缓存、抛异常等。
而新的接口带来的新的调用方式与旧的也不太一样, subscribe
后不再会有 Subscription 也就是如今的 Disposable,为了保持向后的兼容, Flowable 提供了 subscribeWith方法
返回当前的 Subscriber
对象, 并且同时提供了 DefaultSubscriber
, ResourceSubscriber
, DisposableSubscriber
,让他们提供了 Disposable
接口, 可以完成和以前类似的代码:
ResourceSubscriber<Integer> subscriber = new ResourceSubscriber<Integer>() { @Overridepublic void onStart() {request(Long.MAX_VALUE);}@Overridepublic void onNext(Integer t) {System.out.println(t);}@Overridepublic void onError(Throwable t) {t.printStackTrace();}@Overridepublic void onComplete() {System.out.println("Done");} };Flowable.range(1, 10).delay(1, TimeUnit.SECONDS).subscribe(subscriber);subscriber.dispose();
收回 create 方法权限
在RxJava 1.x 最明显的问题就是由于 create 的太过开放,导致其被开发者滥用,而不是学习使用提供的操作符。
并且用户对 RxJava 不够了解,导致各种各样的问题,如背压、异常处理等。
由于规范要求所有的操作符强制支持背压,因此新的 create 采用了保守的设计,让用户实现 FlowableOnSubscribe
接口,并选取背压策略,然后在内部实现封装支持背压,简单的例子如下:
Flowable.create((FlowableEmitter<Integer> emitter) -> { emitter.onNext(1);emitter.onNext(2);emitter.onComplete(); }, BackpressureStrategy.BUFFER);
Functions可以抛出异常
新的 ActionX
、 FunctionX
的方法声明都增加了一个 throws Exception
,这带来了显而易见的好处,现在我们可以这样写:
Flowable.just("file.txt") .map(name -> Files.readLines(name)) .subscribe(lines -> System.out.println(lines.size()), Throwable::printStackTrace);
而在以前是不行的, 因为 Files.readLines(name)
会显式的抛出一个 IOException
。这样对 lambda 更加友好,而不必再去 try catch 。
Scheduler可以直接schedule
在以前是必须要先 createWorker
,用 Worker 对象去 shedule, 现在可以直接在 Scheduler
用这些方法:
public abstract class Scheduler {public Disposable scheduleDirect(Runnable task) { ... }public Disposable scheduleDirect(Runnable task, long delay, TimeUnit unit) { ... }public Disposable scheduleDirectPeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { ... }public long now(TimeUnit unit) { ... }// ... rest is the same: lifecycle methods, worker creation }
这算是一个小优化,方便开发者使用。
Observable的一些继承并入了Flowable中
如 ConnectableObservable
、 BlockObservable
等,这样可以直接在 Flowable
中写出这样的代码:
List<Integer> list = Flowable.range(1, 100).toList().blockingFirst();
其他修改
还有一些普通开发者不太在意的修改:
- hook方式变化,现在可以通过提供接口在 runtime hook
- 部分在 1.x 中 被标记
@Beta
、@Experimental
的操作符现在合并到正式版里了 - 由于类结构的变动,一些类名的变化
等其他变动。
结语
RxJava 作为开源的经典之作,笔者一直都有所关注。后续笔者会继续为大家带来 RxJava 的源码解析与进阶使用系列等。感谢大家的阅读,如有不知之处,欢迎讨论交流。
浅谈RxJava与2.0的新特性相关推荐
- 高性能数据库引擎 CoolHash 产品宣言 Fourinone4.0版新特性(转)
Fourinone4.0版新特性:一个高性能的数据库引擎CoolHash(酷哈嘻) 一.前言:如何写一个数据库 如果将操作系统和业务应用之间的软件都统称中间件的话,那么最重要的软件无疑是数据库,它比w ...
- .NET 4.0 Interop新特性ICustomQueryInterface (转载)
.NET 4.0 Interop新特性ICustomQueryInterface 在.NET Framework v4.0发布的新功能中,在名字空间System.Runtime.InteropServ ...
- .NET Framework 4.0的新特性
本文将揭示.NET 4.0中的3个新特性:图表控件.SEO支持以及ASP.NET 4可扩展的输出缓存. 图表控件 微软向开发者提供了大量可免费下载的图表控件,可以在.NET 3.5 ASP.NET或W ...
- mysql 5.0 php_PHP 5.0的新特性
PHP 5.0的新特性 最近,读者可以从PHP 4.x版本转移到PHP 5.0版本.正如读者期望的那样,在一个新的主要版本中,它做出了一些重要变更.在这个版本中,PHP后台的Zend引擎经过了完全的重 ...
- Tensorflow 2.0的新特性
Tensorflow 2.0的新特性 几天前,Tensorflow刚度过自己的3岁生日,作为当前最受欢迎的机器学习框架,Tensorflow在这个宝座上已经盘踞了近三年.无论是成熟的Keras,还是风 ...
- java 7.0 特性_JDK7.0语法新特性
JDK7.0语法新特性 1,菱形语法(泛型实例化类型自动推断) List list = new ArrayList<>(); // <>这个真的很像菱形 2,在目前版本中,不可 ...
- 盘点Greenplum 6.0六大新特性及展望
导读:本文介绍Greenplum 6.0的新特性. 作者:王春波 来源:大数据DT(ID:hzdashuju) Greenplum 6.0于2019年9月4日正式发布,内核版本从PostgreSQL ...
- C# 8.0 的新特性概览和讲解
本文转自 https://blog.csdn.net/hez2010/article/details/84036742 C# 8.0 的新特性概览和讲解 前言 新的改变 可空引用类型(Nullable ...
- jdk5.0的新特性
jdk的版本在1.4后变化很大,所以叫jdk5.0 下面是总结jdk5.0的新特性: (1)泛型(***) 泛型简介 泛型是J2SE 5.0最重要的特性.他们让你写一个type(类或接口)和创建一个 ...
最新文章
- python代码示例下载-43个Python代码打包下载
- android下拉刷新和上拉加载的一个简单库
- 15、修改和删除触发器(DROP TRIGGER)
- 数字孪生技术从概念走向实际应用
- iOS11新增版本判断API
- 小鹤双拼入门和小鹤音形的搜狗输入法配置方法
- 无线传感器网络物理层
- 【Python】PDF转图片
- 112、工作繁忙,随口胡说;接近胡说,敷衍而已
- MySQL,从删库到跑路
- Data()笔记之getDay()的基本用法
- 挑战微信,主打细分人群社交的超信有机会吗?
- Win10搭建ELK8.0.0环境
- 世界上第一台计算机釆用的主要是什么电子管,基础知识-网络教育
- echarts 保存/导出图片
- 华硕笔记本k555拆机图解_「华硕k401n」华硕K401笔记本电脑拆机清灰步骤详解 - seo实验室...
- 英语口语测试对话软件,英语口语人机对话软件
- Transform 被废弃,ASM 如何适配?
- 09年中国城市房价排行榜出炉 北京位居第4位
- Android转发所有短信-工具:TranspondSms