本文参考:https://www.jianshu.com/p/464fa025229e 这个作者写的特别好。
1.定义
RxJava是响应式编程(Reactive Extensions)在JVM平台上的实现,即用Java语言实现的一套基于观察者模式的异步编程接口。
响应式编程是一种基于异步数据流概念的编程模式。数据流就像一条河:它可以被观测,被过滤,被操作,或者为新的消费者与另外一条流合并为一条新的流。
响应式编程的一个关键概念是事件。事件可以被等待,可以触发过程,也可以触发其它事件。事件是唯一的以合适的方式将我们的现实世界映射到我们的软件中:如果屋里太热了我们就打开一扇窗户。同样的,当我们的天气app从服务端获取到新的天气数据后,我们需要更新app上展示天气信息的UI;汽车上的车道偏移系统探测到车辆偏移了正常路线就会提醒驾驶者纠正,就是是响应事件。
今天,响应式编程最通用的一个场景是UI:我们的移动App必须做出对网络调用、用户触摸输入和系统弹框的响应。在这个世界上,软件之所以是事件驱动并响应的是因为现实生活也是如此。
2.引用
在app的gradle文件中添加这两行代码即可。
implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
3.使用
3.1 Observable和Observer
先看这样一张图:
上面是一根水管,称为上游,下面是一根水管,称为下游。上游把事件送到下游,这就是RxJava的基本原理,基本原理了解后剩下的知识不过是对原理的修修补补。
这两根水管连接起来,上游发送事件1,2,3,而下游则依次接受到事件1,2,3。
在RxJava中,上游被称为Observable(观察得到的),下游被称为Observer(观察者),连接Observable与Observer的方法被称为subscribe(),用RxJava写的话就是这样:
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
}
});
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer value) {
Log.i(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
observable.subscribe(observer);
打印Log如下所示:
2019-04-17 14:04:34.496 15783-15783/? I/RxJavaTestHaHa: onSubscribe:
2019-04-17 14:04:34.496 15783-15783/? I/RxJavaTestHaHa: onNext: 1
2019-04-17 14:04:34.496 15783-15783/? I/RxJavaTestHaHa: onNext: 2
2019-04-17 14:04:34.496 15783-15783/? I/RxJavaTestHaHa: onNext: 3
2019-04-17 14:04:34.496 15783-15783/? I/RxJavaTestHaHa: onComplete:
需要注意的是,只有Observable与Observer连接起来时,Observable才会发送事件,即subscribe()调用时,Observable才开始发送事件。
RxJava中的链式操作则如下所示:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
}
}).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer value) {
Log.i(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
});
3.2 ObservableEmitter和Disposable
在上面的代码中,除过Observable与Observer外,还混入了两个奇怪的东西,ObservableEmitter和Disposable。
Emitter是发射器的意思,而ObservableEmiiter的含义便可想而知了,用来发射事件的。 ObservableEmitter可以发送三种类型的事件,通过调用emitter的onNext(T value)、onComplete()和onError(Throwable error)就可以分别发出next事件、complete事件和error事件。在我们发射时还必须满足以下几个规则:
1.上游可以发送无限个onNext, 下游也可以接收无限个onNext。
2.当上游发送了一个onComplete后, 上游onComplete之后的事件将会继续发送, 而下游收到onComplete事件之后将不再继续接收事件。
3.当上游发送了一个onError后, 上游onError之后的事件将继续发送, 而下游收到onError事件之后将不再继续接收事件。
4.上游可以不发送onComplete或onError事件。
5.最为关键的是onComplete和onError必须唯一并且互斥, 即不能发多个onComplete, 也不能发多个onError, 也不能先发一个onComplete, 然后再发一个onError, 反之亦然。
上面的几条都好理解,关键是最后一条,这是需要我们在代码中控制的,虽然违反最后一条并不一定导致程序崩溃。比如发送多个onComplete是可以正常运行的, 依然是收到第一个onComplete就不再接收了, 但若是发送多个onError, 则收到第二个onError事件会导致程序会崩溃。
一图一表胜千言,这几张图都特别好理解,比看图写作文还简单。
事件 | 示意图 |
---|---|
只发送onNext事件 | |
发送onComplete事件 | |
发送onError事件 |
接下来是你了,Disposable,字面意思是一次性的,可任意处理的。可以将Disposable看作是两根管道中的一个机关,当调用dispose()方法时,它会将两根管道切断,从而下游接收不到上游的事件,但上游还会继续发送剩余的事件。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
Log.i(TAG, "subscribe: 1");
e.onNext(1);
Log.i(TAG, "subscribe: 2");
e.onNext(2);
Log.i(TAG, "subscribe: 3");
e.onNext(3);
Log.i(TAG, "subscribe: complete");
e.onComplete();
Log.i(TAG, "subscribe: 4");
e.onNext(4);
}
}).subscribe(new Observer<Integer>() {
private Disposable disposable;
private int count;
@Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "onSubscribe: ");
disposable = d;
}
@Override
public void onNext(Integer value) {
Log.i(TAG, "onNext: " + value);
count++;
if (count == 2) {
disposable.dispose();
Log.i(TAG, "onNext: dispose");
Log.i(TAG, "onNext: " + disposable.isDisposed());
}
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
});
看一下Log日志:
2019-04-17 14:57:31.621 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: onSubscribe:
2019-04-17 14:57:31.621 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: 1
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: onNext: 1
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: 2
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: onNext: 2
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: onNext: dispose
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: onNext: true
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: 3
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: complete
2019-04-17 14:57:31.622 24679-24679/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: 4
从Log中可以看到,当收到2事件后,便切断了水管连接,但是并没有影响上游发送3,complete,4这几个事件,同时可以看到onSubscribe()方法时最先调用的。
除过这个之外,我们用的水管连接subscribe()有多个重载的方法
public final Disposable subscribe() {}
public final Disposable subscribe(Consumer<? super T> onNext) {}
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {}
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}
public final void subscribe(Observer<? super T> observer) {}
仔细了解一下这些方法,还是很有意思的。比如第一个无参数的方法代表你发任你发,我能处理算我输,而之后的几个方法则比较专注,只处理某种类型的事件,下面就是一个只关注onNext事件的例子。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.i(TAG, "accept: " + integer);
}
});
Log如下所示:
2019-04-17 15:10:46.009 27329-27329/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: 1
2019-04-17 15:10:46.009 27329-27329/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: 2
2019-04-17 15:10:46.009 27329-27329/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: 3
3.3 RxJava中的线程操作
在默认情况下,RxJava中的Observable与Observer都运行在主线程中,试一下。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
Log.i(TAG, "subscribe: " + Thread.currentThread().getName());
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.i(TAG, "accept: " + integer);
Log.i(TAG, "accept: " + Thread.currentThread().getName());
}
});
然后再看一下Log:
2019-04-17 15:50:10.363 2421-2421/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: 1
2019-04-17 15:50:10.363 2421-2421/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: main
2019-04-17 15:50:10.363 2421-2421/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: main
都是在主线程,没错,但我们实际使用时,希望Observable与Observer可以自由运行在任何线程,因为使用场景总是复杂多变的,这个可以做到。
先看两个方法。
subscribeOn() 指定的是上游发送事件的线程,,observeOn() 指定的是下游接收事件的线程。
多次指定上游的线程只有第一次指定的有效,,也就是说多次调用subscribeOn() 只有第一次的有效, 其余的会被忽略。
多次指定下游的线程是可以的,,也就是说每调用一次observeOn() , 下游的线程就会切换一次。
当然,在RxJava中也内置了多种线程选线供我们使用。这些内置的Scheduler已经足够满足我们开发的需求, 因此我们应该使用内置的这些选项,,在RxJava内部使用的是线程池来维护这些线程, 所以效率也比较高。
名称 | 含义 |
---|---|
Schedulers.io() | 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作 |
Schedulers.computation() | 代表CPU计算密集型的操作, 例如需要大量计算的操作 |
Schedulers.newThread() | 代表一个常规的新线程 |
AndroidSchedulers.mainThread() | 代表Android的主线程 |
我们使用一个例子,把上面的知识点全部过一遍。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
Log.i(TAG, "subscribe: " + Thread.currentThread().getName());
}
}).subscribeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.i(TAG, "accept: " + integer);
Log.i(TAG, "accept: " + Thread.currentThread().getName());
}
});
代码不难,结合Log看更加容易理解,这里的RxCachedThreadScheduler-1就是IO线程:
2019-04-17 16:01:54.332 4094-4169/com.xupt.will.rxjavatest I/RxJavaTestHaHa: subscribe: RxCachedThreadScheduler-1
2019-04-17 16:01:54.360 4094-4094/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: 1
2019-04-17 16:01:54.360 4094-4094/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: main
3.4 实践
我在逛掘金时,经常看见有人将RxJava和Retrofit结合,进行网络操作,特别优雅,今天我也试一次。
要求很简单,使用RxJava+Retrofit访问hao123,返回页面数据。
在使用之前,先添加引用。
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
//String转换器
implementation 'com.squareup.retrofit2:converter-scalars:2.0.0'
//RxJava适配器
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
public interface Api {
@GET("/")
Observable<String> getHao123();
}
public class Net {
public static Retrofit create() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.readTimeout(10, TimeUnit.SECONDS);
builder.connectTimeout(10, TimeUnit.SECONDS);
return new Retrofit.Builder().baseUrl("https://www.hao123.com")
.client(builder.build())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
Retrofit retrofit = Net.create();
Api api = retrofit.create(Api.class);
api.getHao123()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, "accept: " + s);
}
});
代码就是这些,然后看一下Log:
2019-04-17 16:37:53.639 9591-9591/com.xupt.will.rxjavatest I/RxJavaTestHaHa: accept: <!DOCTYPE html><html><head><noscript><meta http-equiv="refresh" content="0; UR
可以看到,很优雅,也很方便。
你以为RxJava就是这些吗?这只是开胃菜,压轴的还在后面。