RxJava 2.0
来源:互联网 发布:恒创的域名服务器 编辑:程序博客网 时间:2024/05/29 19:30
前言
之前写RxJava相关文章的时候,就有人想让我谈谈RxJava2.0的新特性,说实话,一开始我是拒绝的。因为在我看来,RxJava2.0虽然是版本的重大升级,但总归还是RxJava,升级一个版本还能上天是咋的?了解一下它的更新文档不就好了么?真的有必要单出一篇文章来谈这个么?
但是详细的了解了RxJava2.0以及部分源码之后,我觉得还是有必要对RxJava2.0做一个说明,帮助大家对于RxJava有更好的认识。
铺垫
假如你还不是很熟悉RxJava,或者对于背压这个概念(2.0更新中会涉及到背压的概念)很模糊,希望你也能读一读下面两篇铺垫的文章:
- 关于RxJava最友好的文章
- 关于RxJava最友好的文章—-背压
关于背压的那篇文章本来是本文的一部分,因为篇幅过大,被剥离出去了,所以建议大家有时间也一并阅读。
正文
RxJava2.0有很多的更新,一些改动甚至冲击了我之前的文章里的内容,这也是我想写这篇文章的原因之一。不过想要写这篇文章其实也是有难度的,因为相关的资料去其实是很少的,还得自己硬着头皮上….不过俗话说得好,有困难要上,没有困难创造困难也要上。
在这里,我会按照我们之前关于RxJava的文章的讲述顺序:观察者模式,操作符,线程调度,这三个方面依次看有哪些更新。
添加依赖
这个估计得放在最前面。
Android端使用RxJava需要依赖新的包名:
//RxJava的依赖包(我使用的最新版本) compile 'io.reactivex.rxjava2:rxjava:2.0.1' //RxAndroid的依赖包 compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
观察者模式
首先声明, RxJava以观察者模式为骨架,在2.0中依然如此 。
不过此次更新中,出现了两种观察者模式:
- Observable(被观察者)/Observer(观察者)
- Flowable(被观察者)/Subscriber(观察者)
RxJava2.X中, Observeable用于订阅Observer ,是不支持背压的,而 Flowable用于订阅Subscriber ,是支持背压(Backpressure)的。
关于背压这个概念以及它在1.0版本中的缺憾在上一篇文章中我已经介绍到了,如果你不是很清楚,我在这里在做一个介绍: 背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略 ,在1.0中,关于背压最大的遗憾,就是集中在Observable这个类中,导致有的Observable支持背压,有的不支持。为了解决这种缺憾,新版本把支持背压和不支持背压的Observable区分开来。
Observable/Observer
Observable正常用法:
Observable mObservable=Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> e) throws Exception { e.onNext(1); e.onNext(2); e.onComplete(); } });Observer mObserver=new Observer<Integer>() { //这是新加入的方法,在订阅后发送数据之前, //回首先调用这个方法,而Disposable可用于取消订阅 @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Integer value) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } };mObservable.subscribe(mObserver);
这种观察者模型是不支持背压的。
啥叫不支持背压呢?
当被观察者快速发送大量数据时,下游不会做其他处理,即使数据大量堆积,调用链也不会报MissingBackpressureException,消耗内存过大只会OOM
我在测试的时候,快速发送了100000个整形数据,下游延迟接收,结果被观察者的数据全部发送出去了,内存确实明显增加了,遗憾的是没有OOM。
所以,当我们使用Observable/Observer的时候,我们需要考虑的是,数据量是不是很大(官方给出以1000个事件为分界线,仅供各位参考)
Flowable/Subscriber
Flowable.range(0,10) .subscribe(new Subscriber<Integer>() { Subscription sub; //当订阅后,会首先调用这个方法,其实就相当于onStart(), //传入的Subscription s参数可以用于请求数据或者取消订阅 @Override public void onSubscribe(Subscription s) { Log.w("TAG","onsubscribe start"); sub=s; sub.request(1); Log.w("TAG","onsubscribe end"); } @Override public void onNext(Integer o) { Log.w("TAG","onNext--->"+o); sub.request(1); } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onComplete() { Log.w("TAG","onComplete"); } });
输出如下:
onsubscribe startonNext--->0onNext--->1onNext--->2...onNext--->10onCompleteonsubscribe end
Flowable是支持背压的,也就是说,一般而言,上游的被观察者会响应下游观察者的数据请求,下游调用request(n)来告诉上游发送多少个数据。这样避免了大量数据堆积在调用链上,使内存一直处于较低水平。
当然,Flowable也可以通过creat()来创建:
Flowable.create(new FlowableOnSubscribe<Integer>() { @Override public void subscribe(FlowableEmitter<Integer> e) throws Exception { e.onNext(1); e.onNext(2); e.onNext(3); e.onNext(4); e.onComplete(); } } //需要指定背压策略 , BackpressureStrategy.BUFFER);
Flowable虽然可以通过create()来创建,但是你必须指定背压的策略,以保证你创建的Flowable是支持背压的(这个在1.0的时候就很难保证,可以说RxJava2.0收紧了create()的权限)。
根据上面的代码的结果输出中可以看到,当我们调用subscription.request(n)方法的时候,不等onSubscribe()中后面的代码执行,就会立刻执行到onNext方法,因此,如果你在onNext方法中使用到需要初始化的类时,应当尽量在subscription.request(n)这个方法调用之前做好初始化的工作;
当然,这也不是绝对的,我在测试的时候发现,通过create()自定义Flowable的时候,即使调用了subscription.request(n)方法,也会等onSubscribe()方法中后面的代码都执行完之后,才开始调用onNext。
TIPS: 尽可能确保在request()之前已经完成了所有的初始化工作,否则就有空指针的风险。
其他观察者模式
当然,除了上面这两种观察者,还有一类观察者
- Single/SingleObserver
- Completable/CompletableObserver
- Maybe/MaybeObserver
其实这三者都差不多,Maybe/MaybeObserver可以说是前两者的复合体,因此以Maybe/MaybeObserver为例简单介绍一下这种观察者模式的用法
//判断是否登陆Maybe.just(isLogin()) //可能涉及到IO操作,放在子线程 .subscribeOn(Schedulers.newThread()) //取回结果传到主线程 .observeOn(AndroidSchedulers.mainThread()) .subscribe(new MaybeObserver<Boolean>() { @Override public void onSubscribe(Disposable d) { } @Override public void onSuccess(Boolean value) { if(value){ ... }else{ ... } } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
上面就是Maybe/MaybeObserver的普通用法,你可以看到,实际上,这种观察者模式并不用于发送大量数据,而是发送单个数据,也就是说,当你只想要某个事件的结果(true or false)的时候,你可以用这种观察者模式
这是上面那些被观察者的上层接口:
//Observable接口interface ObservableSource<T> { void subscribe(Observer<? super T> observer);}//Single接口interface SingleSource<T> { void subscribe(SingleObserver<? super T> observer);}//Completable接口interface CompletableSource { void subscribe(CompletableObserver observer);}//Maybe接口interface MaybeSource<T> { void subscribe(MaybeObserver<? super T> observer);}//Flowable接口public interface Publisher<T> { public void subscribe(Subscriber<? super T> s);}
其实我们可以看到,每一种观察者都继承自各自的接口,这也就把他们能完全的区分开,各自独立(特别是Observable和Flowable),保证了他们各自的拓展或者配套的操作符不会相互影响。
例如flatMap操作符实现:
//Flowable中flatMap的定义Flowable<R> flatMap(Function<? super T, ? extends Publisher<? extends R>> mapper);//Observable中flatMap的定义Observable<R> flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper);
假如你想为Flowable写一个自定义的操作符,那么只要保证Function< Publisher >中的类型实现了Publisher接口即可。这么说可能很抽象,大家不理解其实也没关系,因为并不推荐大家自定义操作符,RxJava中的操纵符的组合已经可以满足大家的需求了。
当然,你也会注意到上面那些接口中的subscribe()方法的返回类型为void了,在1.X中,这个方法一般会返回一个Subscription对象,用于取消订阅。现在,这个功能的对象已经被放到观察者Observer或者subscriber的内部实现方法中了,
Flowable/Subscriber
public interface Subscriber<T> { public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete();}public interface Subscription { public void request(long n); public void cancel();}
上面的实例中,onSubscribe(Subscription s)传入的参数s就肩负着取消订阅的功能,当然,他也可以用于请求上游的数据。
在Observable/observer中,传入的参数是另一个对象
Observable/Observer
public interface Observer<T> { void onSubscribe(Disposable d); void onNext(T value); void onError(Throwable e); void onComplete();}public interface Disposable { /** * Dispose the resource, the operation should be idempotent. */ void dispose(); /** * Returns true if this resource has been disposed. * @return true if this resource has been disposed */ boolean isDisposed();}
在Observer接口中,onSubscribe(Disposable d)方法传入的Disposable也是用于取消订阅,基本功能是差不多的,只不过命名不一致,大家知道就好。
其实这种设计可以说还是符合逻辑的,因为取消订阅这个动作就只有观察者(Observer等)才能做的,现在把它并入到观察者内部,也算顺理成章吧。
最后再提一点更新,就是被观察者不再接收null作为数据源了。
操作符相关
这一块其实可以说没什么改动,大部分之前你用过的操作符都没变,即使有所变动,也只是包名或类名的改动。大家可能经常用到的就是Action和Function。
Action相关
之前我在文章里介绍过关于Action这类接口,在1.0中,这类接口是从Action0,Action1…往后排(数字代表可接受的参数),现在做出了改动
Rx1.0———–Rx2.0
Action1——–Action
Action1——–Consumer
Action2——–BiConsumer
后面的Action都去掉了,只保留了ActionN
Function相关
同上,也是命名方式的改变
上面那两个类, 和RxJava1.0相比,他们都增加了throws Exception,也就是说,在这些方法做某些操作就不需要try-catch 。
例如:
Flowable.just("file.txt").map(name -> Files.readLines(name)).subscribe(lines -> System.out.println(lines.size()), Throwable::printStackTrace);
Files.readLines(name)这类io方法本来是需要try-catch的,现在直接抛出异常,就可以放心的使用lambda ,保证代码的简洁优美。
doOnCancel/doOnDispose/unsubscribeOn
以doOnCancel为例,大概就是当取消订阅时,会调用这个方法,例如:
Flowable.just(1, 2, 3).doOnCancel(() -> System.out.println("Cancelled!")).take(2).subscribe(System.out::println);
take新操符会取消后面那些还未被发送的事件,因而会触发doOnCancel
其他的一些操作符基本没变,或者只是改变了名字,在这里就不一一介绍了,需要提一下的是,很多操作符都有两套,一套用于Observable,一套用于Flowable。
线程调度
可以说这一块儿基本也没有改动,如果一定要说的话。
- 那就是去掉了Schedulers.immediate()这个线程环境
- 移除的还有Schedulers.test()(我好像从来没用过这个方法)
- io.reactivex.Scheduler这个抽象类支持直接调度自定义线程任务(这个我也没怎么用)
补充
如果你想把自己的RxJava1.0的迁移到2.0的版本,可以使用这个库RxJava2Interop ,在github上可以找到,它可以在Rxjava1.0和2.0之间相互转换,也就是说,不仅可以把1.0的代码迁移到2.0,你还可以把2.0的代码迁移到1.0,哈哈。
结尾
其实从整篇文章的分析来看,改动最大的还是观察者模式的实现,被拆分和细化了,主要分成了Observable和Flowable两大类,当然还有与之相关联的其他变动, 总体来看这一版本可以说是对于观察者和被观察者的重构 。
RxJava2.0的范例代码我没精力去写了,也正巧有位外国朋友已经写了RxJava2.0的demo,下面是他的项目地址:
在github上搜索:RxJava2-Android-Samples
当然,学习2.0 的过程中有什么问题也可以在这里留言讨论。
附录
下面我截图展示一下2.0相对于1.0的一些改动的细节,仅做参考。
其实这些都是官方给出的列表,截图在这里只是方便大家观摩。
来自:http://www.jianshu.com/p/220955eefc1f
- RxJava 2.0
- RxJava 2.0来啦!
- RxJava 2.0来啦!
- RxJava 2.0 使用详解
- RxJava 2.0 使用详解
- RxJava 2.0 使用详解
- Rxjava 2.0教程
- RxJava 2.0: flatMap()
- RxJava 2.0 : Flow Control
- RxJava 2.0(一)
- RxJava 2.0(二)
- RxJava 2.0(三)
- rxjava
- RXJava
- RXJava
- RXJava
- RxJava
- Rxjava
- android--menu showAsAction的使用
- Java基础知识_day06_(螺旋矩阵,控制台打印图案)
- 面试题(正在不断补充中)
- 标题:买不到的数目
- RHCS分布式文件存储搭建
- RxJava 2.0
- C++重载、覆盖与隐藏
- Vue路由详解
- Tomcat 调优
- LEX和YACC的使用
- Codeforces Round #405 (rated, Div. 1, based on VK Cup 2017 Round 1) 题解(待续)
- CAD可以用数位板吗?矢量图有必要用数位板吗?
- webview详解
- 【Spring Boot】Spring Boot