RxJava是一个实现异步操作的库,它可以通过链式调用使代码逻辑变得清晰简洁。它的github地址为:
在项目中可以通过以下方式引入依赖:
compile 'io.reactivex.rxjava2:rxjava:2.2.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
RxJava是采用观察者模式来实现的。RxJava具有四个基本概念:observable(被观察者)、observer(观察者)、subscribe(订阅)和事件。observable与observer通过subscribe()方法实现订阅关系,从而observable可以在需要的时候发出事件来通知observer。
Observer observer = new Observer() {
Disposable disposable;
@Override
public void onError(Throwable e) {
Log.e("tag", "onError");
}
@Override
public void onComplete() {
Log.e("tag", "onComplete");
}
@Override
public void onSubscribe(Disposable d) {
Log.e("tag", "onSubscribe");
disposable = d;
}
@Override
public void onNext(String s) {
Log.e("tag", "onNext=" + s);
if ("!".equals(s)) {
disposable.dispose();
}
}
};
上面代码中的Disposable对象可用于中止接收数据,如onNext方法中的判断,当接收到的数据为!时就不再接收数据了。除了Observer接口外,RxJava还有一个Consumer接口,即消费者,它的用法如下:
Consumer consumer = new Consumer() {
@Override
public void accept(String s) throws Exception {
}
};
它只有一个accept方法,事件将会在这个方法内进行处理。
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
subscriber.onNext("Hello");
subscriber.onNext("World");
subscriber.onCompleted();
}
});
此外,还可以通过just(T …)与fromArray(T [ ])方法获取Observable对象,它们的用法如下:
//将依次调用onNext("Hello");onNext("World");onComplete();
Observable observable1 = Observable.just("Hello", "World");
//同just()
Observable observable2 = Observable.fromArray(new String[]{"Hello", "World"});
just方法与fromArray方法的区别在于just只能发送不超过10个事件,而fromArray没有这个限制。
observable.subscribe(observer);
//或
observable.subscribe(consumer);
在不指定线程的情况下,RxJava遵循线程不变原则,即在哪个线程调用subscribe()方法,就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果要切换线程,就需要用到Scheduler(调度器)。在RxJava中,Scheduler相当于线程控制器,RxJava通过它来指定每一段代码应该运行在什么样的线程中。
有了这几个Scheduler,就可以使用subscribeOn()和observerOn()两个方法对线程进行控制了。subscribeOn()指定Observable(被观察者)所在的线程,或者叫做事件产生的线程;observerOn()指定Observer(观察者)所在的线程,或者叫做时间消费的线程。线程调度的使用代码示例如下:
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext("hello");
emitter.onNext("world");
emitter.onNext("!");
emitter.onComplete();
Log.e("tag", "发送线程为:" + Thread.currentThread().getName());
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
Log.e("tag", s);
Log.e("tag", "接收线程为:" + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
上述代码打印结果为:
2019-03-20 15:06:08.113 24078-24162/com.example.study E/tag: 发送线程为:RxCachedThreadScheduler-1
2019-03-20 15:06:08.144 24078-24078/com.example.study E/tag: hello
2019-03-20 15:06:08.144 24078-24078/com.example.study E/tag: 接收线程为:main
2019-03-20 15:06:08.144 24078-24078/com.example.study E/tag: world
2019-03-20 15:06:08.144 24078-24078/com.example.study E/tag: 接收线程为:main
2019-03-20 15:06:08.144 24078-24078/com.example.study E/tag: !
2019-03-20 15:06:08.144 24078-24078/com.example.study E/tag: 接收线程为:main
除了灵活的变换,RxJava另一个牛逼的地方,就是线程的自由控制。前面提到可以使用subscribeOn()与observerOn()来实现线程控制,那么能不能多切换几次线程呢?答案是:能!因为observerOn指定的是Subscriber的线程,而这个Subscriber并不一定是subscirbe()参数中的Subscriber,而是observerOn()执行时的当前Observable所对应的Subscriber,即它的直接下级Subscriber。换句话说,observerOn()指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次observerOn()即可。不同于observerOn(),subscribeOn()的位置放在哪里都可以,但是它只能调用一次,若有多个subscribeOn()时,只有第一个起作用。
过滤操作符,可以将被观察者发送过来的事件依据一定的条件进行筛选,只有筛选合格的事件才会传递给观察者处理,其用法如下:
Observable.just("a", "b", "c", "d").filter(new Predicate() {
@Override
public boolean test(String s) throws Exception {
return "d".equals(s);
}
}).subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
System.out.println("accept:"+s);
}
});
打印结果为:
accept:d
可将被观察者发送的数据转换成其他类型的数据。如下代码可将一个图片资源Id转化为Bitmap对象:
Observable.just(R.mipmap.ic_launcher).map(new Function() {
@Override
public Bitmap apply(Integer integer) throws Exception {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), integer);
return bitmap;
}
}).subscribe(new Consumer() {
@Override
public void accept(Bitmap bitmap) throws Exception {
}
});
flatMap也能实现map操作符的功能,可以完成数据类型的转换,不同之处在于map只能完成一对一的转换,而flatMap可以实现一对多的转换。比如我们有一个小组Group,Group对象内部有一个小组内部有多个Student,我们就可以用flatMap完成Group到Student转换。具体代码实现如下:
public class Group {
private int id;
private List students;
public Group(int id) {
this.id = id;
students = new ArrayList<>();
for (int i = 0; i < 5; i++) {
String name = "Group" + id + "_" + i;
Student student = new Student(name);
students.add(student);
}
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List getStudents() {
return students;
}
public void setStudents(List students) {
this.students = students;
}
}
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Observable.just(new Group(1))
.flatMap(new Function>() {
@Override
public ObservableSource apply(Group group) throws Exception {
return Observable.fromIterable(group.getStudents());
}
}).subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Student student) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
这样,我们就能将1个Group对象转换为多个Student对象发送给观察者了。
在RxJava中会遇到一种情况就是被观察者发送消息十分迅速以致于观察者不能及时响应这些消息,这可能造成事件的无限堆积,最后造成OOM,这个问题即Backpressure(背压)。所谓的背压就是这生产者的速度大于消费者的速度带来的问题。那么,应该怎么处理这个问题呢?
在原来的RxJava1.x版本中Backpressure问题是由Observable来处理的。在RxJava 2.x中对于Backpressure的处理进行了改动,为此将原来的Observable拆分成了新的Observable和Flowable,原先的Observable已经不具备背压处理的能力了。而Flowable是一个被观察者,与Subscriber配合使用,解决背压问题。
注意:处理背压的策略仅仅是处理Subscribe接收事件的方式,并不影响Flowable发送事件的方法。即使采用了处理Backpressure的策略,Flowable原来以什么速度产生事件,现在还是什么样的速度不变,主要处理的是Subscriber接收事件的方式。
什么样的情况下会产生背压问题呢?
处理背压一般有以下几种策略:
这种方式会在产生背压问题的时候直接抛出一个异常,这个异常是MissingBackpressureException。示例代码如下:
Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(FlowableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onNext(4);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onSubscribe(Subscription s) {
Log.e("tag","onSubscribe");
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
Log.e("tag","onNext:"+integer);
}
@Override
public void onError(Throwable t) {
Log.e("tag","onError:"+t.getLocalizedMessage());
}
@Override
public void onComplete() {
Log.e("tag","onComplete");
}
});
上述代码中Subscriber的onSubscribe()方法的参数不再是Disposable而是Subscription,我们可以通过Subscription.cancel()方法切断观察者与被观察者之间的联系。在onSubscribe()方法中,我们调用了s.request(Long.MAX_VALUE)方法,这个方法是用来向生成者申请可以消费的事件数,当调用此方法后,生产者便发送相应数量的事件供消费者消费。若不显式调用request()方法,则默认消费能力为0。
在异步调用时,RxJava中有一个缓存池用于缓存消费者处理不了暂时缓存下来的数据,缓存池的默认大小为128,即只能缓存128个事件。无论request()中传入的数字比128大或小,缓存池在一开始都会存入128个事件。当然如果本身事件不足128个,则不会存128个事件。
在ERROR策略下,如果缓存池溢出,就会立即抛出MissingBackpressureException异常。
不使用背压,没有缓存,等同于Flowable与Subscriber同一线程时的效果。
BUFFER策略是缓存所有的数据,直到观察者处理。也就是不设置缓存大小,如果观察者处理不及时,也会出现OOM,等同于使用Observable。
DROP策略在缓存池缓存不了事件后,就将后续的事件丢弃。消费者通过request()传入需求n,生产者把n个事件传递给消费者消费,其它消费不了的事件就丢掉。
LATEST策略超出缓存大小时,会用新的数据覆盖老的数据。
最后,我们如果不使用create()方法创建Flowable对象,而使用range()、interval等操作符创建时就不能配置BackpressStrategy了,这个时候RxJava提供了onBackpressureBuffer()、onBackpressureDrop、onBackpressureLatest()方法配置背压策略。使用方法如下:
Flowable.range(0,300)
.onBackpressureDrop()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onSubscribe(Subscription s) {
}
@Override
public void onNext(Integer integer) {
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
});
参考资料: