rxjava的线程控制(调度/切换):subscribeOn()/ observerOn()
1.rxjava线程调度控制的作用:
rxjava通过线程调度可以控制Observable(被观察者)/观察者(Observer)工作的线程的类型
2.为什么要进行rxjava的线程的调度切换:
在rxjava中被观察者(Observable) / 观察者(Observer)的工作线程 = 创建自身的线程
即在安卓中如果被观察者(Observable) / 观察者(Obesrver)在主线程被创建那么它们也工作(发送事件 / 接收响应事件) 在主线程中
通过以下的例子观察被观察者Observable / 观察者Observer的工作线程:
//观察Rxjava中被观察者Observable/观察者Observer的默认工作线程
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();
Log.d(TAG, "Observable被观察者当前的工作线程为: " + Thread.currentThread().getName());
}
}).subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe: 开始采用subscribe连接 thread = " + Thread.currentThread().getName());
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer + " thread = " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: " + e.getMessage() + " thread = " + Thread.currentThread().getName());
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete: thread = " + Thread.currentThread().getName());
}
});
打印结果(Observable和observer都工作在创建它们的线程中):
以上这种操作在安卓中的问题:
由于在安卓中主线程是不能做耗时操作的(比如:联网操作,大文件的IO, 数据库查询等)否则会导致ANR异常的产生
因此在安卓中我们一般会采用开启新的线程配合Handler 或者采用AsyncTask的方式在子线程中进行耗时的操作然后采用
Handler发消息或者AsyncTask回调到主线程中进行更新UI的操作,因此rxjava的Observable被观察者和观察者
Observer工作在创建它们的线程中的这种方式在安卓中是有一定问题的,由此我们就需要引入rxjava的线程调度的控制符
也就是需要实现以下的这种操作:
被观察者Observable在子线程中产生事件发送数据(实现耗时的操作)
观察者Observer在安卓主线程中订阅并响应子线程中Observable发送的事件(如更新UI的操作)
若需要解决以上的问题需要用到rxjava的调度器Scheduler配合我们的subscribeOn()/ observeOn()操作来控制调度线程
功能性操作符observeOn() / subscribeOn()功能操作符
以上两个操作符的作用是控制观察者Observer / 被观察者Observable工作的线程类型
在rxjava和rxAndroid中Schedulers/ AndroidSchedulers内置了几种线程类型:
类型 | 含义 |
| 用于IO密集型的操作,例如读写SD卡文件,查询数据库,访问网络等,具有线程缓存机制,在此调度器接收到任务后,先检查线程缓存池中,是否有空闲的线程,如果有,则复用,如果没有则创建新的线程,并加入到线程池中,如果每次都没有空闲线程使用,可以无上限的创建新线程。 |
| 在每执行一个任务时创建一个新的线程,不具有线程缓存机制,因为创建一个新的线程比复用一个线程的成本更高,虽然使用Schedulers.io( )的地方,都可以使用Schedulers.newThread( ),但是,Schedulers.newThread( )的效率没有Schedulers.io( )高。 |
| 拥有一个线程单例,所有的任务都在这一个线程中执行,当此线程中有任务执行时,其他任务将会按照先进先出的顺序依次执行。 |
| 在当前线程立即执行任务,如果当前线程有任务在执行,则会将其暂停,等插入进来的任务执行完之后,再将未完成的任务接着执行。 |
| 用于CPU 密集型计算任务,即不会被 I/O 等操作限制性能的耗时操作,例如xml,json文件的解析,Bitmap图片的压缩取样等,具有固定的线程池,大小为CPU的核数。不可以用于I/O操作,因为I/O操作的等待时间会浪费CPU。 |
| 在Android UI线程中执行任务,为Android开发定制。 |
| 指定一个线程调度器,由此调度器来控制任务的执行策略. |
注:
1. 以上的线程rxjava都是用线程池来进行维护的因此线程调度的效率都比较高
2.在RxJava2中,废弃了RxJava1中的Schedulers.immediate( )
在RxJava1中,Schedulers.immediate( )的作用为在当前线程立即执行任务,功能等同于RxJava2中的Schedulers.trampoline( )
而Schedulers.trampoline( )在RxJava1中的作用是当其它排队的任务完成后,在当前线程排队开始执行接到的任务,
有点像RxJava2中的Schedulers.single(),但也不完全相同,因为Schedulers.single()不是在当前线程而是在一个线程
单例中排队执行任务.
具体使用:
Observable.subscribeOn(Schedulers.Thread):指定被观察者Observable 发送事件的线程(传入RxJava内置的线程类型)
Observable.observeOn(Schedulers.Thread):指定观察者Observer 接收 & 响应事件的线程(传入RxJava内置的线程类型)
通过订阅(subscribe)连接观察者和被观察者
Observable.subscribeOn(Schedulers.newThread()) // 1. 指定被观察者Observable 生产事件的线程
.observeOn(AndroidSchedulers.mainThread()) // 2. 指定观察者Observer 接收 & 响应事件的线程
.subscribe(observer); // 3. 最后再通过订阅(subscribe)连接观察者Observer和被观察者Observable
1.使用一次subscribeOn() 和使用一次observeOn() 进行线程调度
//使用一次subscribeOn() 和 observeOn()的线程调度
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 4; i++) {
Log.e(TAG, "上游被观察者Observable在" + Thread.currentThread().getName() + "线程, 生产一个事件: " + i );
SystemClock.sleep(1000);
e.onNext(i);
}
e.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.d(TAG, "下游观察者Observer在" + Thread.currentThread().getName() + "线程接收响应到了事件: " + integer);
}
});
打印结果:
2.使用两次subscribeOn()和一次observeOn()进行线程调度
//使用两次subscribeOn() 和 一次observeOn()的线程调度
//通过两次设置subscribeOn()发射和处理数据在不同的线程,但是最终起作用的是第一次设置的工作线程
//由此可以得出多次设置subscribeOn()设置被观察者Observable工作的线程最终起作用的是第一次设置的线程
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 4; i++) {
Log.e(TAG, "上游被观察者Observable在" + Thread.currentThread().getName() + "线程, 生产一个事件: " + i );
SystemClock.sleep(1000);
e.onNext(i);
}
e.onComplete();
}
}).subscribeOn(Schedulers.newThread())
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(@NonNull Integer integer) throws Exception {
Log.w(TAG, "map操作符在" + Thread.currentThread().getName() + "线程处理了事件: " + integer);
return integer * 2;
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.d(TAG, "下游观察者Observer在" + Thread.currentThread().getName() + "线程接收响应到了事件: " + integer);
}
});
打印结果:
3.使用一次subscribeOn() 和两次observeOn()
//使用一次subscribeOn() 和两次 observeOn()进行线程调度
//可以看到每次通过observeOn()设置观察者Observer的响应接收上游Observable发射的事件的线程都是起作用的
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 4; i++) {
Log.e(TAG, "上游被观察者Observable在" + Thread.currentThread().getName() + "线程, 生产一个事件: " + i );
SystemClock.sleep(1000);
e.onNext(i);
}
e.onComplete();
}
}).subscribeOn(Schedulers.computation()) //指定Observable被观察者在computation线程上生产并发射事件
.observeOn(Schedulers.io()) //指定观察者Observer在io线程上进行map变换操作
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(@NonNull Integer integer) throws Exception {
Log.w(TAG, "map操作符在" + Thread.currentThread().getName() + "线程处理了事件: " + integer);
return integer * 2;
}
}).observeOn(AndroidSchedulers.mainThread()) //在指定观察者Observer在main主线程进行数据的最终接收
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.d(TAG, "下游观察者Observer在" + Thread.currentThread().getName() + "线程接收响应到了事件: " + integer);
}
});
打印结果:
通过以上的三个示例可以总结出subscribeOn()和observeOn()操作符的使用特点:
subscribeOn()来指定上游(Observable)对数据的处理运行在特定的线程调度器Scheduler上,直到遇到observeOn()
改变线程调度器若多次设定,则只有第一次起作用。
observeOn()指定下游(Observer)操作运行在特定的线程调度器Scheduler上。若多次设定,每次均起作用。
4.Schedulers.trampoline() 线程
我们可以发现虽然Observer在接收到数据后,延迟了两秒才处理,但是Observable依然在Observer将数据处理
完之后才开始发射下一条。
Schedulers.trampoline()的作用在当前线程立即执行任务,如果当前线程有任务在执行,则会将其暂停,等插入进
来的任务执行完之后,再将未完成的任务接着执行。
//使用trampoline进行线程的调度实现上游发射一个数据 下游就对应的接收一个数据
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 4; i++) {
Log.e(TAG, "上游被观察者Observable在" + Thread.currentThread().getName() + "线程, 生产一个事件: " + i );
SystemClock.sleep(500);
e.onNext(i);
}
e.onComplete();
}
}).subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.trampoline())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
SystemClock.sleep(1000);
Log.d(TAG, "下游观察者Observer在" + Thread.currentThread().getName() + "线程接收响应到了事件: " + integer);
}
});
打印结果:
5.Schedulers.single()线程操作符
//使用single()进行线程的调度
//如果使用single()线程那么执行的顺序是一一来执行的
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 4; i++) {
Log.e(TAG, "上游被观察者Observable在" + Thread.currentThread().getName() + "线程, 生产一个事件: " + i );
SystemClock.sleep(500);
e.onNext(i);
}
e.onComplete();
}
}).subscribeOn(Schedulers.single())
.observeOn(Schedulers.single())
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(@NonNull Integer integer) throws Exception {
Log.w(TAG, "map操作符在" + Thread.currentThread().getName() + "线程处理了事件: " + integer);
return integer * 2;
}
})
.observeOn(Schedulers.single())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Log.d(TAG, "下游观察者Observer在" + Thread.currentThread().getName() + "线程接收响应到了事件: " + integer);
}
});
打印结果:
如果不需要线程的执行结果我们可以直接使用Schedulers来开启线程执行异步耗时的操作
public void schedulers_demo(View view){
mWorker = Schedulers.io().createWorker().schedule(new Runnable() {
@Override
public void run() {
SystemClock.sleep(4000);
Log.d(TAG, "Schedulers调度器的使用: " + Thread.currentThread().getName());
}
});
}
关于subscribeOn()和observeOn()的一些使用建议看一下示例:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
//工作在计算线程
Log.d(TAG, "subscribe: thread = " + Thread.currentThread().getName());
e.onNext(1);
e.onNext(2);
e.onComplete();
}
}).observeOn(Schedulers.io())
.flatMap(new Function<Integer, ObservableSource<Integer>>() {
@Override
public ObservableSource<Integer> apply(@NonNull final Integer integer) throws Exception {
//工作在io线程
Log.d(TAG, "apply : thread = " + Thread.currentThread().getName());
return Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Integer> e) throws Exception {
//工作在主线程
Log.d(TAG, "subscribe flatmap+: thread = " + Thread.currentThread().getName());
e.onNext(integer * 2);
}
}).subscribeOn(AndroidSchedulers.mainThread());
}
})
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(@NonNull Integer integer) throws Exception {
//工作在主线程
Log.d(TAG, "apply map : thread = " + Thread.currentThread().getName());
return integer * 10;
}
}).subscribeOn(Schedulers.computation())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
//工作在主线程
Log.d(TAG, "accept: 接收 " + integer + " thread = " + Thread.currentThread().getName());
}
});
注:
示意图中,不同的颜色代表着代码执行时候工作在不同的线程中
黄色: Schedulers.computation() 计算线程
粉色:Schedulers.io() io线程
红褐色:Android 主线程