引入依赖: implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
// (see https://github.com/ReactiveX/RxJava/releases for latest 2.x.x version)
implementation 'io.reactivex.rxjava2:rxjava:2.x.x'
接口变化
RxJava 2.x 拥有了新的特性,其依赖于4个基础接口,它们分别是
- Publisher
- Subscriber
- Subscription
- Processor
其中最核心的莫过于 Publisher
和 Subscriber
。Publisher
可以发出一系列的事件,而 Subscriber
负责和处理这些事件。
其中用的比较多的自然是 Publisher
的 Flowable
,它支持背压。关于背压给个简洁的定义就是:
背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。
简而言之,背压是流速控制的一种策略。有兴趣的可以看一下官方对于背压的讲解。
可以明显地发现,RxJava 2.x 最大的改动就是对于 backpressure
的处理,为此将原来的 Observable
拆分成了新的 Observable
和 Flowable
,同时其他相关部分也同时进行了拆分,但令人庆幸的是,是它,是它,还是它,还是我们最熟悉和最喜欢的 RxJava。
观察者模式
大家可能都知道, RxJava 以观察者模式为骨架,在 2.0 中依旧如此。
不过此次更新中,出现了两种观察者模式:
- Observable ( 被观察者 ) / Observer ( 观察者 )
- Flowable (被观察者)/ Subscriber (观察者)
在 RxJava 2.x 中,Observable 用于订阅 Observer,不再支持背压(1.x 中可以使用背压策略),而 Flowable 用于订阅 Subscriber , 是支持背压(Backpressure)的。
Observable
在 RxJava 1.x 中,我们最熟悉的莫过于 Observable
这个类了,笔者在刚刚使用 RxJava 2.x 的时候,创建了 一个 Observable
,瞬间一脸懵逼有木有,居然连我们最最熟悉的 Subscriber
都没了,取而代之的是 ObservableEmmiter
,俗称发射器。此外,由于没有了Subscriber
的踪影,我们创建观察者时需使用 Observer
。而 Observer
也不是我们熟悉的那个 Observer
,又出现了一个 Disposable
参数带你装逼带你飞。
废话不多说,从会用开始,还记得 RxJava 的三部曲吗?
** 第一步:初始化 Observable **
** 第二步:初始化 Observer **
** 第三步:建立订阅关系 **
Observable.create(new ObservableOnSubscribe() { // 第一步:初始化Observable
@Override
public void subscribe(@NonNull ObservableEmitter e) throws Exception {
Log.e(TAG, "Observable emit 1" + "\n");
e.onNext(1);
Log.e(TAG, "Observable emit 2" + "\n");
e.onNext(2);
Log.e(TAG, "Observable emit 3" + "\n");
e.onNext(3);
e.onComplete();
Log.e(TAG, "Observable emit 4" + "\n" );
e.onNext(4);
}
}).subscribe(new Observer() { // 第三步:订阅
// 第二步:初始化Observer
private int i;
private Disposable mDisposable;
@Override
public void onSubscribe(@NonNull Disposable d) {
mDisposable = d;
}
@Override
public void onNext(@NonNull Integer integer) {
i++;
if (i == 2) {
// 在RxJava 2.x 中,新增的Disposable可以做到切断的操作,让Observer观察者不再接收上游事件
mDisposable.dispose();
}
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "onError : value : " + e.getMessage() + "\n" );
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete" + "\n" );
}
});
不难看出,RxJava 2.x 与 1.x 还是存在着一些区别的。首先,创建 Observable
时,回调的是 ObservableEmitter
,字面意思即发射器,并且直接 throws Exception。其次,在创建的 Observer 中,也多了一个回调方法:onSubscribe
,传递参数为Disposable
,Disposable
相当于 RxJava 1.x 中的 Subscription
, 用于解除订阅。可以看到示例代码中,在 i 自增到 2 的时候,订阅关系被切断。
当然,我们的 RxJava 2.x 也为我们保留了简化订阅方法,我们可以根据需求,进行相应的简化订阅,只不过传入对象改为了 Consumer
。
Consumer
即消费者,用于接收单个值,BiConsumer
则是接收两个值,Function
用于变换对象,Predicate
用于判断。这些接口命名大多参照了 Java 8 ,熟悉 Java 8 新特性的应该都知道意思,这里也不再赘述。
线程调度
关于线程切换这点,RxJava 1.x 和 RxJava 2.x 的实现思路是一样的。这里简单的说一下,以便于我们的新司机入手。
subScribeOn
同 RxJava 1.x 一样,subscribeOn
用于指定 subscribe()
时所发生的线程,从源码角度可以看出,内部线程调度是通过 ObservableSubscribeOn
来实现的。
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler));
}
ObservableSubscribeOn
的核心源码在 subscribeActual
方法中,通过代理的方式使用 SubscribeOnObserver
包装 Observer
后,设置 Disposable
来将 subscribe
切换到 Scheduler
线程中。
observeOn
observeOn
方法用于指定下游 Observer
回调发生的线程。
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableObserveOn(this, scheduler, delayError, bufferSize));
}
线程切换需要注意的
RxJava 内置的线程调度器的确可以让我们的线程切换得心应手,但其中也有些需要注意的地方。
- 简单地说,
subscribeOn()
指定的就是发射事件的线程,observerOn
指定的就是订阅者接收事件的线程。 - 多次指定发射事件的线程只有第一次指定的有效,也就是说多次调用
subscribeOn()
只有第一次的有效,其余的会被忽略。 - 但多次指定订阅者接收线程是可以的,也就是说每调用一次
observerOn()
,下游的线程就会切换一次。
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(@NonNull ObservableEmitter e) throws Exception {
Log.e(TAG, "Observable thread is : " + Thread.currentThread().getName());
e.onNext(1);
e.onComplete();
}
}).subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.e(TAG, "After observeOn(mainThread),Current thread is " + Thread.currentThread().getName());
}
})
.observeOn(Schedulers.io())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.e(TAG, "After observeOn(io),Current thread is " + Thread.currentThread().getName());
}
});
输出:
07-03 14:54:01.177 15121-15438/com.nanchen.rxjava2examples E/RxThreadActivity: Observable thread is : RxNewThreadScheduler-1
07-03 14:54:01.178 15121-15121/com.nanchen.rxjava2examples E/RxThreadActivity: After observeOn(mainThread),Current thread is main
07-03 14:54:01.179 15121-15439/com.nanchen.rxjava2examples E/RxThreadActivity: After observeOn(io),Current thread is RxCachedThreadScheduler-2
实例代码中,分别用 Schedulers.newThread()
和 Schedulers.io()
对发射线程进行切换,并采用 observeOn(AndroidSchedulers.mainThread()
和 Schedulers.io()
进行了接收线程的切换。可以看到输出中发射线程仅仅响应了第一个 newThread
,但每调用一次 observeOn()
,线程便会切换一次,因此如果我们有类似的需求时,便知道如何处理了。
RxJava 中,已经内置了很多线程选项供我们选择,例如有:
-
Schedulers.io()
代表io操作的线程, 通常用于网络,读写文件等io密集型的操作; -
Schedulers.computation()
代表CPU计算密集型的操作, 例如需要大量计算的操作; -
Schedulers.newThread()
代表一个常规的新线程; -
AndroidSchedulers.mainThread()
代表Android的主线程
这些内置的 Scheduler
已经足够满足我们开发的需求,因此我们应该使用内置的这些选项,而 RxJava 内部使用的是线程池来维护这些线程,所以效率也比较高。
操作符
关于操作符,在官方文档中已经做了非常完善的讲解,并且笔者前面的系列教程中也着重讲解了绝大多数的操作符作用,这里受于篇幅限制,就不多做赘述,只挑选几个进行实际情景的讲解。
map
map
操作符可以将一个 Observable
对象通过某种关系转换为另一个Observable
对象。在 2.x 中和 1.x 中作用几乎一致,不同点在于:2.x 将 1.x 中的 Func1
和 Func2
改为了 Function
和 BiFunction
。
采用 map 操作符进行网络数据解析
想必大家都知道,很多时候我们在使用 RxJava 的时候总是和 Retrofit 进行结合使用,而为了方便演示,这里我们就暂且采用 OkHttp3 进行演示,配合 map
,doOnNext
,线程切换进行简单的网络请求:
1)通过 Observable.create() 方法,调用 OkHttp 网络请求;
2)通过 map 操作符集合 gson,将 Response 转换为 bean 类;
3)通过 doOnNext() 方法,解析 bean 中的数据,并进行数据库存储等操作;
4)调度线程,在子线程中进行耗时操作任务,在主线程中更新 UI ;
5)通过 subscribe(),根据请求成功或者失败来更新 UI 。
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(@NonNull ObservableEmitter e) throws Exception {
Builder builder = new Builder()
.url("http://api.avatardata.cn/MobilePlace/LookUp?key=ec47b85086be4dc8b5d941f5abd37a4e&mobileNumber=13021671512")
.get();
Request request = builder.build();
Call call = new OkHttpClient().newCall(request);
Response response = call.execute();
e.onNext(response);
}
}).map(new Function() {
@Override
public MobileAddress apply(@NonNull Response response) throws Exception {
if (response.isSuccessful()) {
ResponseBody body = response.body();
if (body != null) {
Log.e(TAG, "map:转换前:" + response.body());
return new Gson().fromJson(body.string(), MobileAddress.class);
}
}
return null;
}
}).observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer() {
@Override
public void accept(@NonNull MobileAddress s) throws Exception {
Log.e(TAG, "doOnNext: 保存成功:" + s.toString() + "\n");
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull MobileAddress data) throws Exception {
Log.e(TAG, "成功:" + data.toString() + "\n");
}, new Consumer() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "失败:" + throwable.getMessage() + "\n");
}
});
concat
concat
可以做到不交错的发射两个甚至多个 Observable 的发射事件,并且只有前一个 Observable
终止(onComplete
) 后才会订阅下一个 Observable
。
采用 concat 操作符先读取缓存再通过网络请求获取数据
想必在实际应用中,很多时候(对数据操作不敏感时)都需要我们先读取缓存的数据,如果缓存没有数据,再通过网络请求获取,随后在主线程更新我们的UI。
concat
操作符简直就是为我们这种需求量身定做。
利用 concat
的必须调用 onComplete
后才能订阅下一个 Observable
的特性,我们就可以先读取缓存数据,倘若获取到的缓存数据不是我们想要的,再调用 onComplete()
以执行获取网络数据的 Observable
,如果缓存数据能应我们所需,则直接调用 onNext()
,防止过度的网络请求,浪费用户的流量。
Observable cache = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(@NonNull ObservableEmitter e) throws Exception {
Log.e(TAG, "create当前线程:"+Thread.currentThread().getName() );
FoodList data = CacheManager.getInstance().getFoodListData();
// 在操作符 concat 中,只有调用 onComplete 之后才会执行下一个 Observable
if (data != null){ // 如果缓存数据不为空,则直接读取缓存数据,而不读取网络数据
isFromNet = false;
Log.e(TAG, "\nsubscribe: 读取缓存数据:" );
runOnUiThread(new Runnable() {
@Override
public void run() {
mRxOperatorsText.append("\nsubscribe: 读取缓存数据:\n");
}
});
e.onNext(data);
}else {
isFromNet = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
mRxOperatorsText.append("\nsubscribe: 读取网络数据:\n");
}
});
Log.e(TAG, "\nsubscribe: 读取网络数据:" );
e.onComplete();
}
}
});
Observable network = Rx2AndroidNetworking.get("http://www.tngou.net/api/food/list")
.addQueryParameter("rows",10+"")
.build()
.getObjectObservable(FoodList.class);
// 两个 Observable 的泛型应当保持一致
Observable.concat(cache,network)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull FoodList tngouBeen) throws Exception {
Log.e(TAG, "subscribe 成功:"+Thread.currentThread().getName() );
if (isFromNet){
mRxOperatorsText.append("accept : 网络获取数据设置缓存: \n");
Log.e(TAG, "accept : 网络获取数据设置缓存: \n"+tngouBeen.toString() );
CacheManager.getInstance().setFoodListData(tngouBeen);
}
mRxOperatorsText.append("accept: 读取数据成功:" + tngouBeen.toString()+"\n");
Log.e(TAG, "accept: 读取数据成功:" + tngouBeen.toString());
}
}, new Consumer() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "subscribe 失败:"+Thread.currentThread().getName() );
Log.e(TAG, "accept: 读取数据失败:"+throwable.getMessage() );
mRxOperatorsText.append("accept: 读取数据失败:"+throwable.getMessage()+"\n");
}
});
有时候我们的缓存可能还会分为 memory 和 disk ,实际上都差不多,无非是多写点 Observable
,然后通过 concat
合并即可。
flatMap 实现多个网络请求依次依赖
想必这种情况也在实际情况中比比皆是,例如用户注册成功后需要自动登录,我们只需要先通过注册接口注册用户信息,注册成功后马上调用登录接口进行自动登录即可。
我们的 flatMap
恰好解决了这种应用场景,flatMap
操作符可以将一个发射数据的 Observable
变换为多个 Observables
,然后将它们发射的数据合并后放到一个单独的 Observable
,利用这个特性,我们很轻松地达到了我们的需求。
Rx2AndroidNetworking.get("http://www.tngou.net/api/food/list")
.addQueryParameter("rows", 1 + "")
.build()
.getObjectObservable(FoodList.class) // 发起获取食品列表的请求,并解析到FootList
.subscribeOn(Schedulers.io()) // 在io线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) // 在主线程处理获取食品列表的请求结果
.doOnNext(new Consumer() {
@Override
public void accept(@NonNull FoodList foodList) throws Exception {
// 先根据获取食品列表的响应结果做一些操作
Log.e(TAG, "accept: doOnNext :" + foodList.toString());
mRxOperatorsText.append("accept: doOnNext :" + foodList.toString()+"\n");
}
})
.observeOn(Schedulers.io()) // 回到 io 线程去处理获取食品详情的请求
.flatMap(new Function>() {
@Override
public ObservableSource apply(@NonNull FoodList foodList) throws Exception {
if (foodList != null && foodList.getTngou() != null && foodList.getTngou().size() > 0) {
return Rx2AndroidNetworking.post("http://www.tngou.net/api/food/show")
.addBodyParameter("id", foodList.getTngou().get(0).getId() + "")
.build()
.getObjectObservable(FoodDetail.class);
}
return null;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull FoodDetail foodDetail) throws Exception {
Log.e(TAG, "accept: success :" + foodDetail.toString());
mRxOperatorsText.append("accept: success :" + foodDetail.toString()+"\n");
}
}, new Consumer() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "accept: error :" + throwable.getMessage());
mRxOperatorsText.append("accept: error :" + throwable.getMessage()+"\n");
}
});
善用 zip 操作符,实现多个接口数据共同更新 UI
在实际应用中,我们极有可能会在一个页面显示的数据来源于多个接口,这时候我们的 zip
操作符为我们排忧解难。
zip
操作符可以将多个 Observable
的数据结合为一个数据源再发射出去。
Observable observable1 = Rx2AndroidNetworking.get("http://api.avatardata.cn/MobilePlace/LookUp?key=ec47b85086be4dc8b5d941f5abd37a4e&mobileNumber=13021671512")
.build()
.getObjectObservable(MobileAddress.class);
Observable observable2 = Network.getGankApi()
.getCategoryData("Android",1,1);
Observable.zip(observable1, observable2, new BiFunction() {
@Override
public String apply(@NonNull MobileAddress mobileAddress, @NonNull CategoryResult categoryResult) throws Exception {
return "合并后的数据为:手机归属地:"+mobileAddress.getResult().getMobilearea()+"人名:"+categoryResult.results.get(0).who;
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull String s) throws Exception {
Log.e(TAG, "accept: 成功:" + s+"\n");
}
}, new Consumer() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "accept: 失败:" + throwable+"\n");
}
});
采用 interval 操作符实现心跳间隔任务
想必即时通讯等需要轮训的任务在如今的 APP 中已是很常见,而 RxJava 2.x 的 interval
操作符可谓完美地解决了我们的疑惑。
这里就简单的意思一下轮训。
private Disposable mDisposable;
@Override
protected void doSomething() {
mDisposable = Flowable.interval(1, TimeUnit.SECONDS)
.doOnNext(new Consumer() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
Log.e(TAG, "accept: doOnNext : "+aLong );
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
Log.e(TAG, "accept: 设置文本 :"+aLong );
mRxOperatorsText.append("accept: 设置文本 :"+aLong +"\n");
}
});
}
/**
* 销毁时停止心跳
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mDisposable != null){
mDisposable.dispose();
}
}
RxJava 1.x 如何平滑升级到 RxJava 2.x?
由于 RxJava 2.x 变化较大无法直接升级,幸运的是,官方为我们提供了 RxJava2Interrop 这个库,可以方便地把 RxJava 1.x 升级到 RxJava 2.x,或者将 RxJava 2.x 转回到 RxJava 1.x。
从16年11月份推出RxJava 2.0 ,到现在差不多大半年的时间里,RxJava已经来到了2.x时代,RxJava 1.x 可能也慢慢地被2.x 代替。RxJava 2.x在Reactive-Streams规范的基础上从头开始完全重写,虽然操作符基本没有发生变化,但是因为Reactive-Streams具有不同的架构,因此对一些众所周知的RxJava类型进行了更改。
RxJava 2.x相对与1.x还是有很多的不同,RxJava的类型更改了,很多类的命名和方法的命名发生了变化(可能功能与1.x相同),要想从RxJava 1.x 顺利地过渡到2.x, 就得了解这些变化。因此本教程就带你了解这些变化,从而能更快地上手RxJava2.x。
本篇文章就来先了解一下RxJava 2.x的5种响应类型。
[图片上传失败...(image-55bb3b-1531139819666)]
一、RxJava 2.x中5种类型介绍
1 . Observable and Flowable
关于在RxJava 0.x版本引入背压的一个小的遗憾是,没有设计一个单独的基础反应类,而是对Observable本身进行了改装。背压的主要问题在于热源(如:UI事件),不能合理地反压并导致不可预料的异常MissingBackpressureException,这是初学者不期望看到的。
在RxJava 2.x版本中修复了这种情况,将o.reactivex.Observable
作为非背压,引入新的io.reactivex.Flowable
作为启用背压基础反应类。
好消息是,在2.x版本中,主要的操作符保持不变(同1.x版本),坏消息是,在导入的时候应当小心,它可能会无意的选择非背压的o.reactivex.Observable
.
我们应该选哪种?
当构建数据流(作为RxJava的最终消费者)或考虑您的2.x兼容库应该采取和返回什么类型时,您可以考虑几个因素,以帮助您避免诸如MissingBackpressureException或OutOfMemoryError之类的问题。
Observable使用场景:
- 数据流最长不超过1000个元素,即随着时间的流逝,应用没有机会报OOME(OutOfMemoryError)错误。
- 处理诸如鼠标移动或触摸事件之类的GUI事件
Flowable使用场景:
- 处理超过10K+ 的元素流
- 从磁盘读取(解析文件)
- 从数据库读取数据
- 从网络获取数据流
2 . Single 使用介绍
Single是2.x版本中的一种基础响应类型,Single是从头开始重新设计的,能单独发射一个onSuccess
或者onError
事件,它现在的架构来自于the Reactive-Streams
设计。它的消费者类型已经从接受rx.Subscription
的rx.Single.SingleSubscriber
变为了io.reactivex.SingleObserver
,有3个方法:
interface SingleObserver {
void onSubscribe(Disposable d);
void onSuccess(T value);
void onError(Throwable error);
}
3 . Completable使用介绍
Completeble类型基本保持不变,1.x的版本已经沿着Reactive-Streams风格设计,所以没有用户级别的更改。
相似地命名改变,rx.Completable.CompletableSubscriber
变为带有onSubscribe(Disposable)
方法的io.reactivex.CompletableObserver
:
interface CompletableObserver {
void onSubscribe(Disposable d);
void onComplete();
void onError(Throwable error);
}
4 . Maybe 使用介绍
RxJava 2.0.0-RC2 介绍了一个新的基础响应类型,它叫做Maybe。从概念上来将,它像是 Single和Completable的结合,它可能发射0个或者1个项目,或者一个error信号。
Maybe类通过依赖MaybeSource作为它的基础接口类型MaybeObserver
作为信号响应接口并且遵循协议onSubscribe (onSuccess | onError | onComplete)?因为最多可能发射1个元素,所以Maybe类型没有背压的概念(因为它没有像Flowable和Observable一样有未知长度的可膨胀缓冲区)
这意味着onSubscribe(Disposable)
的调用潜在地跟随着其他onXXX
方法之一的调用,不同于Flowable,如果这儿只有一个信号值发射信号,那么只有onSuccess
被调用,而不会调用complete
。
二、RxJava 2.x中5种类型使用示例
1 . Observable示例
在写示例之前,我们先来回顾一下 1.x 版本是如何创建Observable和如何订阅的:
RxJava 1.x :
//创建 observable
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
subscriber.onNext("hello world");
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
//订阅方式一
observable.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Object o) {
}
});
// 订阅方式二
observable.subscribe(new Action1() {
@Override
public void call(Object o) {
// onNext
}
});
通过create
方法创建Observable,接收一个OnSubscribe
接口,其中有一个回调方法call
,参数为Subscriber
,我们用Subscriber
来发射数据。通过subscribe
方法来订阅,可以接收一个Subscriber 实现全部方法,也可以接收一个Action1
只实现onNext方法。
RxJava 2.x :
//创建Observable
Observable observable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(@NonNull ObservableEmitter e) throws Exception {
Log.e(TAG,"start emitter data");
e.onNext("Hello");
e.onNext("world");
e.onComplete();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
// 订阅方式一:下游消费者 Observer
observable.subscribe(new Observer() {
@Override
public void onSubscribe(@NonNull Disposable d) {
// onSubscribe 是2.x新添加的方法,在发射数据前被调用,相当于1.x的onStart方法
Log.e(TAG,"onSubscribe");
}
@Override
public void onNext(@NonNull String s) {
Log.e(TAG,"onNext");
Log.e(TAG,s);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG,"onError");
}
@Override
public void onComplete() {
Log.e(TAG,"onComplete");
}
});
// 订阅方式二:Consumer
observable.subscribe(new Consumer() {
@Override
public void accept(@NonNull String o) throws Exception {
Log.e(TAG,"consumer:"+o);
}
});
打印结果如下:
06-25 14:31:35.435 21505-21505/com.zhouwei.demoforrxjava2 E/MainActivity: onSubscribe
06-25 14:31:35.437 21505-21853/com.zhouwei.demoforrxjava2 E/MainActivity: start emitter data
06-25 14:31:35.438 21505-21505/com.zhouwei.demoforrxjava2 E/MainActivity: onNext:Hello
06-25 14:31:35.438 21505-21505/com.zhouwei.demoforrxjava2 E/MainActivity: onNext:world
06-25 14:31:35.438 21505-21505/com.zhouwei.demoforrxjava2 E/MainActivity: onComplete
其实我们可以对比一下,1.x 和 2.x 方法都试一样的,只是它们所接收的响应接口改变了,对应变化如下:
RxJava 1.x -> RxJava 2.x
---------------------------------------
OnSubscribe -> ObservableOnSubscribe
Subscriber -> Observer
Subscriber -> ObservableEmitter
Action1 -> Consumer
RxJava 2.x 中对这些接口进行了重新设计,让一个接口的职责更加单一,类的命名和方法命名与它的功能更佳符合(见名知意)。如在1.x 中,Subscriber 既能发射数据,又能消费数据,充当观察者和被观察者。在2.x 中 把它拆解成了2个接口。
ObservableEmitter
专门用来发射数据,Consumer
专门用来消费数据。 除此之外,在RxJava 2.x 中,多了一个void onSubscribe(@NonNull Disposable d)
回调方法,参数为Disposable
,Disposable
是用来解除订阅关系的,这让我们的解除订阅变得更佳容易(比起1.x 通过subscribe
返回 Subscription)。
上面对比了在RxJava 1.x 和2.x 版本创建Observable的方式,其实在RxJava 2.x中,这5种类型的用法是非常相似的,它们的接口命名规则相同,只要你知道其中一种,就知道其他几种类型该如何在上游发射数据和在下游消费数据。create
接收的类型都为xxxOnSubscrible(xxx为5种类型对应的名字),发射器为xxxEmitter,具体如下表:
RxJava 2.x 类型 | create参数(响应接口) | 发射器 | Observer |
---|---|---|---|
Observable | ObservableOnSubscribe | ObservableEmitter | Observer |
Flowable | FlowableOnSubscribe | FlowableEmitter | FlowableSubscriber |
Single | SingleOnSubscribe | SingleEmitter | SingleObserver |
Completable | CompletableOnSubscribe | CompletableEmitter | CompletableObserver |
Maybe | MaybeOnSubscribe | MaybeEmitter | MaybeObserver |
2 . Flowable示例
上面对比了RxJava 1.x 和 2.x 创建使用Observable的方式,并且总结了2.x 相关类的改变,如上面表。那么使用Flowable的方式和Observable是很相似的,看一下代码:
Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(@NonNull FlowableEmitter e) throws Exception {
Log.e(TAG,"start send data ");
for(int i=0;i<100;i++){
e.onNext(i);
}
e.onComplete();
}
}, BackpressureStrategy.DROP)//指定背压策略
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new FlowableSubscriber() {
@Override
public void onSubscribe(@NonNull Subscription s) {
//1, onSubscribe 是2.x新添加的方法,在发射数据前被调用,相当于1.x的onStart方法
//2, 参数为 Subscription ,Subscription 可用于向上游请求发射多少个元素,也可用于取笑请求
//3, 必须要调用Subscription 的request来请求发射数据,不然上游是不会发射数据的。
Log.e(TAG,"onSubscribe...");
subscription = s;
s.request(100);
}
@Override
public void onNext(Integer integer) {
Log.e(TAG,"onNext:"+integer);
}
@Override
public void onError(Throwable t) {
Log.e(TAG,"onError..."+t);
}
@Override
public void onComplete() {
Log.e(TAG,"onComplete...");
}
});
Flowable和Observable的使用基本相同,只不过Observable不支持背压,而Flowable支持背压。使用的时候,还是要注意几个小细节:
1,创建Flowable的时候需要指定一个背压策略,本文使用的是PBackpressureStrategy.DROP(丢弃策略),RxJava 2.x中,内置了5种背压策略,由于篇幅有限,背压和背压策略下一篇拿出来单独讲。
2,onSubscribe 回调方法中,参数是Subscription而不是Disposable,前文说过,RxJava 2.x 中,订阅的管理换成了Disposable,但是Flowable使用的是Subscription,这个Subscription不是1.x 版本中的Subscription,虽然它有取消订阅的能力。主要用于请求上游元素和取消订阅。
3,在使用Flowable的时候,必须调用Subscription 的requsest方法请求,不然上游是不会发射数据的。看request的方法解释:[图片上传失败...(image-ae901f-1531139819664)]
3 . Single、Completable 和 Maybe 示例
Single、Completable和Maybe就比较简单,Single用于只发射一个数据,Completable不发送数据,它给下游发射一个信号。而Maybe则是Single和Completable的结合,根据名字就可以看出,它的结果是不确定的,可能发发射0(Completable)或1(Single) 个元素,或者收到一个Error信号。
Single示例:
Single.create(new SingleOnSubscribe() {
@Override
public void subscribe(@NonNull SingleEmitter e) throws Exception {
Log.e(TAG,"subscribe...");
e.onSuccess(true);
}
})
.observeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e(TAG,"onSubscribe...");
}
@Override
public void onSuccess(@NonNull Boolean aBoolean) {
Log.e(TAG,"onSuccess...");
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG,"onError...");
}
});
Single只发射一个元素,所以没有complete
方法,不像Observable或者Flowable,数据发射完成之后,需要调用complete
告诉下游已经完成。
Completable示例:
Completable.create(new CompletableOnSubscribe() {
@Override
public void subscribe(@NonNull CompletableEmitter e) throws Exception {
Log.e(TAG,"start send data");
e.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e(TAG,"onSubscribe");
}
@Override
public void onComplete() {
Log.e(TAG,"onComplete");
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG,"onError");
}
});
Completable 不会发射数据,只会给下游发送一个信号。回调 onComplete
方法。
Maybe示例:
Maybe.create(new MaybeOnSubscribe() {
@Override
public void subscribe(@NonNull MaybeEmitter e) throws Exception {
Log.e(TAG,"start send data");
e.onSuccess(true);
e.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MaybeObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Log.e(TAG,"onSubscribe");
}
@Override
public void onSuccess(@NonNull Boolean aBoolean) {
Log.e(TAG,"1->onSuccess:"+aBoolean);
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG,"onError");
}
@Override
public void onComplete() {
Log.e(TAG,"onComplete");
}
});
Maybe是Single和Completable的结合,需要注意的是
onSuccess
和onComplete
方法只会执行其中一个,这不同于Observable和Flowable最后是以onComplete()
结尾.
如上面的代码,先调用onSuccess
发射一个元素,再调用onComplete
,
e.onComplete();
e.onSuccess(true);
最后打印结果如下:
E/MainActivity: onSubscribe
E/MainActivity: start send data
E/MainActivity: onComplete
可以看到只回调了 onComplete
,我们把调用的顺序调换一下:
e.onSuccess(true);
e.onComplete();
打印结果如下:
E/MainActivity: onSubscribe
E/MainActivity: start send data
E/MainActivity: 1->onSuccess:true
可以看到调换了之后打印OnSucces()
而没有打印onComplete()
,这也印证了只会回调其中之一。
三、总结
RxJava 2.x 相比于 1.x 还是有很大的变化,虽然操作符基本不变,但是很多类和接口都是基于Reactive-Streams 规范重新设计的,命名也发生了变换,要想玩转RxJava 2.x ,你得了解这些变化和使用场景,本文介绍了RxJava 2.x 的5种基础响应类型,希望对才开始学习RxJava 2.x 的同学有所帮助。
作者:依然范特稀西
链接:https://www.jianshu.com/p/a2aa585ff6fd
來源:
著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
RxJava 到底是什么
一个词:异步。
RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。
然而,对于初学者来说,这太难看懂了。因为它是一个『总结』,而初学者更需要一个『引言』。
其实, RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。
RxJava 好在哪
换句话说,『同样是做异步,为什么人们用它,而不用现成的 AsyncTask / Handler / XXX / ... ?』
一个词:简洁。
异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的 AsyncTask
和Handler
,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。
假设有这样一个需求:界面上有一个自定义的视图 imageCollectorView
,它的作用是显示多张图片,并能使用 addImage(Bitmap)
方法来任意增加显示的图片。现在需要程序将一个给出的目录数组 File[] folders
中每个目录下的 png 图片都加载出来并显示在 imageCollectorView
中。需要注意的是,由于读取图片的这一过程较为耗时,需要放在后台执行,而图片的显示则必须在 UI 线程执行。常用的实现方式有多种,我这里贴出其中一种:
new Thread() {
@Override
public void run() {
super.run();
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
而如果使用 RxJava ,实现方式是这样的:
Observable.from(folders)
.flatMap(new Func1>() {
@Override
public Observable call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
那位说话了:『你这代码明明变多了啊!简洁个毛啊!』大兄弟你消消气,我说的是逻辑的简洁,不是单纯的代码量少(逻辑简洁才是提升读写代码速度的必杀技对不?)。观察一下你会发现, RxJava 的这个实现,是一条从上到下的链式调用,没有任何嵌套,这在逻辑的简洁性上是具有优势的。当需求变得复杂时,这种优势将更加明显(试想如果还要求只选取前 10 张图片,常规方式要怎么办?如果有更多这样那样的要求呢?再试想,在这一大堆需求实现完两个月之后需要改功能,当你翻回这里看到自己当初写下的那一片迷之缩进,你能保证自己将迅速看懂,而不是对着代码重新捋一遍思路?)。
另外,如果你的 IDE 是 Android Studio ,其实每次打开某个 Java 文件的时候,你会看到被自动 Lambda 化的预览,这将让你更加清晰地看到程序逻辑:
Observable.from(folders)
.flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) })
.filter((Func1) (file) -> { file.getName().endsWith(".png") })
.map((Func1) (file) -> { getBitmapFromFile(file) })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });
如果你习惯使用 Retrolambda ,你也可以直接把代码写成上面这种简洁的形式。而如果你看到这里还不知道什么是 Retrolambda ,我不建议你现在就去学习它。原因有两点:1. Lambda 是把双刃剑,它让你的代码简洁的同时,降低了代码的可读性,因此同时学习 RxJava 和 Retrolambda 可能会让你忽略 RxJava 的一些技术细节;2. Retrolambda 是 Java 6/7 对 Lambda 表达式的非官方兼容方案,它的向后兼容性和稳定性是无法保障的,因此对于企业项目,使用 Retrolambda 是有风险的。所以,与很多 RxJava 的推广者不同,我并不推荐在学习 RxJava 的同时一起学习 Retrolambda。事实上,我个人虽然很欣赏 Retrolambda,但我从来不用它。
在Flipboard 的 Android 代码中,有一段逻辑非常复杂,包含了多次内存操作、本地文件操作和网络操作,对象分分合合,线程间相互配合相互等待,一会儿排成人字,一会儿排成一字。如果使用常规的方法来实现,肯定是要写得欲仙欲死,然而在使用 RxJava 的情况下,依然只是一条链式调用就完成了。它很长,但很清晰。
所以, RxJava 好在哪?就好在简洁,好在那把什么复杂逻辑都能穿成一条线的简洁。
API 介绍和原理简析
这个我就做不到一个词说明了……因为这一节的主要内容就是一步步地说明 RxJava 到底怎样做到了异步,怎样做到了简洁。
1. 概念:扩展的观察者模式
RxJava 的异步实现,是通过一种扩展的观察者模式来实现的。
观察者模式
先简述一下观察者模式,已经熟悉的可以跳过这一段。
观察者模式面向的需求是:A 对象(观察者)对 B 对象(被观察者)的某种变化高度敏感,需要在 B 变化的一瞬间做出反应。举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者,小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会漏过任何瞬间。程序的观察者模式和这种真正的『观察』略有不同,观察者不需要时刻盯着被观察者(例如 A 不需要每过 2ms 就检查一次 B 的状态),而是采用注册(Register)或者称为订阅(Subscribe)的方式,告诉被观察者:我需要你的某某状态,你要在它变化的时候通知我。 Android 开发中一个比较典型的例子是点击监听器 OnClickListener
。对设置 OnClickListener
来说, View
是被观察者, OnClickListener
是观察者,二者通过 setOnClickListener()
方法达成订阅关系。订阅之后用户点击按钮的瞬间,Android Framework 就会将点击事件发送给已经注册的 OnClickListener
。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。当然,这也得益于我们可以随意定制自己程序中的观察者和被观察者,而警察叔叔明显无法要求小偷『你在作案的时候务必通知我』。
OnClickListener 的模式大致如下图:
如图所示,通过 setOnClickListener()
方法,Button
持有 OnClickListener
的引用(这一过程没有在图上画出);当用户点击时,Button
自动调用 OnClickListener
的 onClick()
方法。另外,如果把这张图中的概念抽象出来(Button
-> 被观察者、OnClickListener
-> 观察者、setOnClickListener()
-> 订阅,onClick()
-> 事件),就由专用的观察者模式(例如只用于监听控件点击)转变成了通用的观察者模式。如下图:
而 RxJava 作为一个工具库,使用的就是通用形式的观察者模式。
RxJava 的观察者模式
RxJava 有四个基本概念:Observable
(可观察者,即被观察者)、 Observer
(观察者)、 subscribe
(订阅)、事件。Observable
和 Observer
通过 subscribe()
方法实现订阅关系,从而 Observable
可以在需要的时候发出事件来通知 Observer
。
与传统观察者模式不同, RxJava 的事件回调方法除了普通事件 onNext()
(相当于 onClick()
/ onEvent()
)之外,还定义了两个特殊的事件:onCompleted()
和 onError()
。
onCompleted()
: 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext()
发出时,需要触发onCompleted()
方法作为标志。onError()
: 事件队列异常。在事件处理过程中出异常时,onError()
会被触发,同时队列自动终止,不允许再有事件发出。在一个正确运行的事件序列中,
onCompleted()
和onError()
有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()
和onError()
二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。onCompleted()
: 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext()
发出时,需要触发onCompleted()
方法作为标志。onError()
: 事件队列异常。在事件处理过程中出异常时,onError()
会被触发,同时队列自动终止,不允许再有事件发出。在一个正确运行的事件序列中,
onCompleted()
和onError()
有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()
和onError()
二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。onCompleted()
: 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext()
发出时,需要触发onCompleted()
方法作为标志。onError()
: 事件队列异常。在事件处理过程中出异常时,onError()
会被触发,同时队列自动终止,不允许再有事件发出。在一个正确运行的事件序列中,
onCompleted()
和onError()
有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()
和onError()
二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。onCompleted()
: 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext()
发出时,需要触发onCompleted()
方法作为标志。onError()
: 事件队列异常。在事件处理过程中出异常时,onError()
会被触发,同时队列自动终止,不允许再有事件发出。在一个正确运行的事件序列中,
onCompleted()
和onError()
有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()
和onError()
二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。onCompleted()
: 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext()
发出时,需要触发onCompleted()
方法作为标志。onError()
: 事件队列异常。在事件处理过程中出异常时,onError()
会被触发,同时队列自动终止,不允许再有事件发出。在一个正确运行的事件序列中,
onCompleted()
和onError()
有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()
和onError()
二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。