线程切换
背压在计算机系统中指网络拥塞信息逆流通过。在rxjava中理解为:被观察者发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息,从而操作消息的阻塞现象。
在RxJava2.0中官方,推出了Flowable 和Subscriber用来支持背压,同样的去除了Observable对背压的支持,对的就像你上面看到的,Observable不再支持背压了,即使阻塞崩溃也不会抛出MissingBackpressureException
Flowable的用法吧。
Flowable.create(FlowableOnSubscribe source, BackpressureStrategy mode)
FlowableOnSubscribe很好理解就是一个就是Flowable的一个被观察者源,而BackpressureStrategy就是Flowable提供的背压策略
背压策略
Observable.from("url1", "url2", "url3")
.subscribe(new Action1() {
@Override
public void call(String s) {
System.out.println(s);
}
});
Observable.just("Hello, world!") //输入类型 String
.map(new Func1() { //String ---> Integer
@Override
public Integer call(String s) { //参数类型 String
return s.hashCode(); //返回类型 Integer
}
})
.subscribe(new Action1() { //由map()返回的Observable对象调用
@Override
public void call(Integer i) { //参数类型 Integer(即上面的返回类型)
System.out.println(Integer.toString(i));
}
});
这里出现了一个叫做 Func1 的类。它和 Action1 非常相似,也是 RxJava 的一个接口,用于包装含有一个参数的方法。 Func1 和 Action 的区别在于,** Func1 包装的是有返回值的方法**。另外,和 ActionX 一样, FuncX 也有多个,用于不同参数个数的方法。(用于输入与输出参数数据类型的变换)
flatMap()的原理
Student[] students = ...;
Subscriber subscriber = new Subscriber() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName()); //打印课程名
}
...
};
Observable.from(students)
.flatMap(new Func1>() {
@Override
public Observable call(Student student) { //输入参数 student
return Observable.from(student.getCourses()); //返回student对应课程 Observable
}
})
.subscribe(subscriber);
以上例程通过flatMap() 实现打印每一个student的课程(一个student对应多个课程)。
变换的原理 lift()
这些变换虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。而在 RxJava 的内部,它们是基于同一个基础的变换方法: lift(Operator)。
在 Observable 执行了 lift(Operator) 方法之后,会返回一个新的Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理后发送给 Subscriber。不建议自定义Operater()来直接使用lift()
public class LiftAllTransformer implements Observable.Transformer {
@Override
public Observable call(Observable observable) {
return observable
.lift1()
.lift2()
.lift3()
.lift4();
}
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);
在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。也就是说,如果只用上面的方法,实现出来的只是一个同步的观察者模式。观察者模式本身的目的就是『后台处理,前台回调』的异步机制,因此异步对于 RxJava 是至关重要的。
在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件.如果需要切换线程,就需要用到 Scheduler (调度器)。
有了这几个 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。
例程:
Observable.just(1, 2, 3, 4).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Action1() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number); //发生在主线程
}
});
Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(mapOperator) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator2) // IO 线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // Android 主线程,由 observeOn() 指定
通过 observeOn() 的多次调用,程序实现了线程的多次切换。不过,不同于 observeOn() , subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。(因为只有第一次的SubscribeOn()是有效的)
subscribeOn() 和 observeOn() 都做了线程切换的工作(图中的 “schedule…” 部位)。不同的是, subscribeOn() 的线程切换发生在 OnSubscribe 中,即在它通知上一级 OnSubscribe 时,这时事件还没有开始发送,因此 subscribeOn() 的线程控制可以从事件发出的开端就造成影响;而 observeOn() 的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此 observeOn()控制的是它后面的线程。
上游事件是怎么给弄到子线程里去的?
就是直接把订阅方法放在了一个Runnable中去执行,这样就一旦这个Runnable在某个子线程执行,那么上游所有事件只能在这个子线程中执行了。
线程间传递消息也是使用 Handler来实现。
延伸:doOnSubscribe()
虽然超过一个的 subscribeOn() 对事件处理的流程没有影响,但在流程之前却是可以利用的。
onStart() 由于在 subscribe() 发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe() 被调用时的线程。这就导致如果 onStart() 中含有对线程有要求的代码(例如在界面上显示一个 ProgressBar,这必须在主线程执行),将会有线程非法的风险,因为有时你无法预测 subscribe() 将会在什么线程执行。而与 Subscriber.onStart() 相对应的方法 Observable.doOnSubscribe() 。它和Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io()) //事件的发出还是在io线程
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable subscribeOn(Scheduler scheduler) {
//非空判断,若为空会直接抛出异常
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
//这里将Observable和Scheduler封装成了ObservableSubscribeOn
//onAssembly方法返回了这个new ObservableSubscribeOn(this, scheduler)对象
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler));
}
首先subscribeOn方法中将Observable和Scheduler封装成了ObservableSubscribeOn(是Observable的子类)。返回了一个新的Observable,而这个新的Observable里面持有一个上一层Observable的引用。
接着,订阅的时候调用subscribe方法,在subscribe方法中会调用封装的ObservableSubscribeOn的subscribeActual()方法(方法里面会先调用observer的onSubscribe()方法,此时的Observable的subscribe方法发生在当前线程,所以Observer的onSubscribe方法的执行线程和当前调用Observable的subscribe方法的线程一致!!!)
public void subscribeActual(Observer super T> s) {
ObservableSubscribeOn.SubscribeOnObserver parent = new ObservableSubscribeOn.SubscribeOnObserver(s);
s.onSubscribe(parent);
parent.setDisposable(this.scheduler.scheduleDirect(new ObservableSubscribeOn.SubscribeTask(parent)));
}
里面new了一个SubscribeTask, ObservableSubscribeOn.SubscribeTask(parent)(ObservableSubscribeOn.SubscribeTask是一个Runnable),实现了Runnable接口,它的run方法会在subscribeOn指定的Scheduler的线程中执行。当我们的 SubscribeTask 的 run 方法运行在哪个线程,相应的 observable 的 subscribe 方法就运行在哪个线程。
SubscribeTask 包装 parent(SubscribeOnObserver ,包装了 Observer),SubscribeTask 实现了 Runnable 接口,在 run 方法里面调用了 source.subscribe(parent),因而 run 方法所执行的线程将由 worker 决定。这就是 下游决定上游 observable 执行线程的原理。
@CheckReturnValue
@SchedulerSupport("custom")
public final Observable observeOn(Scheduler scheduler) {
return this.observeOn(scheduler, false, bufferSize());
}
@CheckReturnValue
@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));
}
同样地
protected void subscribeActual(Observer super T> observer) {
if (this.scheduler instanceof TrampolineScheduler) {
this.source.subscribe(observer);
} else {
Worker w = this.scheduler.createWorker();
this.source.subscribe(new ObservableObserveOn.ObserveOnObserver(observer, w, this.delayError, this.bufferSize));
}
}
void schedule() {
if (this.getAndIncrement() == 0) {
this.worker.schedule(this);
}
}
总结:
子线程切换主线程:给主线程所在的Handler发消息,然后就把逻辑切换过去了。
主线程切换子线程:把任务放到线程池中执行就能把执行逻辑切换到子线程
子线程切换子线程:把任务分别扔进两个线程就行了。
因为 RxJava 最终能影响 ObservableOnSubscribe 这个匿名实现接口的运行环境的只能是最后一次运行的 subscribeOn() ,又因为 RxJava 订阅的时候是从下往上订阅,所以从上往下第一个 subscribeOn() 就是最后运行的,这就造成了写多个 subscribeOn() 只有第一个subscribeOn()有用。
https://www.jianshu.com/p/88aacbed8aa5
subscribeOn()产生的线程切换发生在代码执行的第二层,而它的回溯又将会执行在新的线程中。因此,在subscribeOn切换线程以后的流程,均将在新的线程中执行。
observeOn()产生的线程切换都发生在第三层执行层,而在切换线程前的业务代码由于已经执行了,故不受切换线程切换的影响。