文章转载自:大神的简书
要在android中使用RxJava2,先添加Gradle配置:
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
RxJava简介:
先假设有两根水管:
产生事件的水管称作上游,即RxJava中的Observable,接受事件的水管称作下游,即RxJava中的Observer,之间的连接关系就是subscribe()。这个关系用RxJava来表示就是:
//创建一个上游 Observable:
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
});
//创建一个下游 Observer
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "subscribe");
}
@Override
public void onNext(Integer value) {
Log.d(TAG, "" + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "error");
}
@Override
public void onComplete() {
Log.d(TAG, "complete");
}
};
//建立连接
observable.subscribe(observer);
注意:只有当上下游建立连接,也就是subscribe()方法执行的时候才开始发送事件。
此外,把这段代码连起来就是RxJava中的链式操作了。即省去了不必要的对象创建。
ObservableEmitter顾名思义为发射器,用来发出三种类型的事件,通过调用其对象的onNext(),onError(),onComplete()三个方法来发出Next,Error,Complete三个事件,需要注意以下规则:
- 上游可以发送无限个onNext,下游也可以无限接受onNext
- 上游发送了一个onError和OnComplete事件之后,可以继续发送剩下的事件,而下游接收到这两个事件之一,就会停止接收事件。
- 上游可以不发送onError和OnComplete事件
- 注意:onError和OnComplete事件的发送必须互斥并且唯一,不能发送多个,也不能同时发送两种事件
Disposable字面意思是一次性用品,可以看作是管道之间的开关,当调用其dispose()方法后,会从管道中间切开,导致下游无法接收到事件。注意:上游不会因为调用该方法停止发送事件。
来看个例子, 我们让上游依次发送1,2,3,complete,4,在下游收到第二个事件之后, 切断水管, 看看运行结果:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "emit 1");
emitter.onNext(1);
Log.d(TAG, "emit 2");
emitter.onNext(2);
Log.d(TAG, "emit 3");
emitter.onNext(3);
Log.d(TAG, "emit complete");
emitter.onComplete();
Log.d(TAG, "emit 4");
emitter.onNext(4);
}
}).subscribe(new Observer<Integer>() {
private Disposable mDisposable;
private int i;
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "subscribe");
mDisposable = d;
}
@Override
public void onNext(Integer value) {
Log.d(TAG, "onNext: " + value);
i++;
if (i == 2) {
Log.d(TAG, "dispose");
mDisposable.dispose();
Log.d(TAG, "isDisposed : " + mDisposable.isDisposed());
}
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "error");
}
@Override
public void onComplete() {
Log.d(TAG, "complete");
}
});
运行结果为:
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: subscribe
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 1
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 1
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 2
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: onNext: 2
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: dispose
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: isDisposed : true
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 3
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit complete
12-02 06:54:07.728 7404-7404/zlc.season.rxjava2demo D/TAG: emit 4
可以看到发送第二个事件以后水管被切断了,但是上游并没有因为发送了OnComplete事件而停止发送,并且下游的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) {}
- 不带任何参数的表示无论上游发送什么下游不管,随意发。
- 带有一个Consumer类型的参数的表示下游只关心上游发送的onNext事件,其他事件不管。
也就可以这样写:
subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "onNext: " + integer);
}
});
RxJava线程控制
通常情况下上下游是在同一线程内工作的,比如在主线程中创建一个Observable发送事件,则上游在主线程中工作,同样在主线程中创建的Observer也在主线程中接收事件。看下面的例子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "Observable thread is : " + Thread.currentThread().getName());
Log.d(TAG, "emit 1");
emitter.onNext(1);
}
});
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "Observer thread is :" + Thread.currentThread().getName());
Log.d(TAG, "onNext: " + integer);
}
};
observable.subscribe(consumer);
}
结果为:
D/TAG: Observable thread is : main
D/TAG: emit 1
D/TAG: Observer thread is :main
D/TAG: onNext: 1
一般我们是在子线程中执行耗时操作,在主线程中更新UI,如下图:
黄色管子代表子线程,蓝色代表主线程,要想做到两个事件在不同的线程中执行,需要用到RxJava内置的线程调度器。
在刚才的例子中,将最后的subscribe()方法修改一下:
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(consumer);
运行结果:
D/TAG: Observable thread is : RxNewThreadScheduler-2
D/TAG: emit 1
D/TAG: Observer thread is :main
D/TAG: onNext: 1
可以看到线程改变了,其中RxNewThread是RxJava内置的一种线程,subscribeOn()指定了上游所在的线程,observeOn()指定了下游所在的线程。
注意:对上游多次调用subscribeOn()指定线程,只有第一次指定起作用,而对于下游多次调用observeOn()指定线程,每次都会改变下游所在的线程。
在RxJava中,内置了很多线程供我们选择,并且这些线程由线程池来维护的,所以效率比较高:
- Schedulers.io()代表IO操作的线程,通常用于网络、读写文件等IO密集的操作
- Schedulers.computation()代表CPU计算密集的线程,例如有大量计算得操作
- Schedulers.newThread代表常规的线程
- AndroidSchedulers.mainThread代表Android主线程
操作符
map
map的作用是对于上游发送的每个事件都应用于一个函数,使得每个事件都按照相应的函数去变化。如图:
图中的Map函数将上游的圆形变换为了下游的正方形,代码表示为:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
return "This is result " + integer;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
运行结果为:
D/TAG: This is result 1
D/TAG: This is result 2
D/TAG: This is result 3
上游的整型数字经过map操作后在下游变成了String类型。
可以看出发来的事件都可以转换为想要的类型,Object,集合等等。
FlatMap
FlatMap作用是将上游发送事件的一个Observable变换为多个发送事件的Observables,再讲它们发送的事件合并到一个Observable中发送。如图:
分解动作:
图中的FlatMap将圆形转换为三角形和正方形,并且对于每个颜色的事件都创建了一个管道,在管道中进行变换,再将转换过的合并发送到下游。
注意:FlatMap并不能保证事件发送的顺序,如果想要保证顺序需要使用concatMap。
代码demo如下:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).flatMap(new Function<Integer, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Integer integer) throws Exception {
final List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("I am value " + integer);
}
return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, s);
}
});
代码中将上游传过来的3个数字每个循环三遍添加到新创建的String管道,并且加了10ms延迟,所以输出结果可以看出不是顺序的。
将上面代码中的flatMap改为concatMap可以发现,结果是顺序的了,接收到的事件和发送的事件顺序一致。
zip
zip函数的作用是将多个Observable发送的事件组合到一起,然后将组合的事件作为整体发送到下游,它严格按照发送的顺序组合,并且发送的事件个数只与多个Observable中数据较少的那个相同。如图:
通过分解动作可以看出:
- 组合严格按照各个Observable发送的事件顺序,对应事件组合,不会出现交叉组合。
- 最终发送的组合事件个数只与Observables中数据最少的相同,因为没有事件能够取出来,就无法组合了。
demo如下:
Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "emit 1");
emitter.onNext(1);
Log.d(TAG, "emit 2");
emitter.onNext(2);
Log.d(TAG, "emit 3");
emitter.onNext(3);
Log.d(TAG, "emit 4");
emitter.onNext(4);
Log.d(TAG, "emit complete1");
emitter.onComplete();
}
});
Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
Log.d(TAG, "emit A");
emitter.onNext("A");
Log.d(TAG, "emit B");
emitter.onNext("B");
Log.d(TAG, "emit C");
emitter.onNext("C");
Log.d(TAG, "emit complete2");
emitter.onComplete();
}
});
Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer integer, String s) throws Exception {
return integer + s;
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(String value) {
Log.d(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
运行结果:
D/TAG: onSubscribe
D/TAG: emit 1
D/TAG: emit 2
D/TAG: emit 3
D/TAG: emit 4
D/TAG: emit complete1
D/TAG: emit A
D/TAG: onNext: 1A
D/TAG: emit B
D/TAG: onNext: 2B
D/TAG: emit C
D/TAG: onNext: 3C
D/TAG: emit complete2
D/TAG: onComplete
可以看出组合发送了,但是第一个Observable发送完以后才开始组合,这是因为两个Observable在同一个线程中,线程的执行肯定有先后顺序,采用上面的subscribeOn()方法将两个Observable放到不同的线程中执行,这样就可以实现一一结合了,结果如下:
D/TAG: onSubscribe
D/TAG: emit A
D/TAG: emit 1
D/TAG: onNext: 1A
D/TAG: emit B
D/TAG: emit 2
D/TAG: onNext: 2B
D/TAG: emit C
D/TAG: emit 3
D/TAG: onNext: 3C
D/TAG: emit complete2
D/TAG: onComplete
可以发现Observable1中还有两个事件没有发送,因为已经没有组合对象了,发送也没有意义,前面是因为在一个线程中,所以会一次性发送完,在不同线程中,即使不发送complete,那两个事件也不会发送。
zip实践
一个界面需要展示用户的一些信息,需要从两个服务器中抓取数据,要用到zip操作符来组合抓取的信息。
首先分别定义两个请求接口:
public interface Api {
@GET
Observable<UserBaseInfoResponse> getUserBaseInfo(@Body UserBaseInfoRequest request);
@GET
Observable<UserExtraInfoResponse> getUserExtraInfo(@Body UserExtraInfoRequest request);
}
然后用zip来打包请求:
Observable<UserBaseInfoResponse> observable1 =
api.getUserBaseInfo(new UserBaseInfoRequest()).subscribeOn(Schedulers.io());
Observable<UserExtraInfoResponse> observable2 =
api.getUserExtraInfo(new UserExtraInfoRequest()).subscribeOn(Schedulers.io());
Observable.zip(observable1, observable2,
new BiFunction<UserBaseInfoResponse, UserExtraInfoResponse, UserInfo>() {
@Override
public UserInfo apply(UserBaseInfoResponse baseInfo,
UserExtraInfoResponse extraInfo) throws Exception {
return new UserInfo(baseInfo, extraInfo);
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<UserInfo>() {
@Override
public void accept(UserInfo userInfo) throws Exception {
//do something;
}
});