前言
理解线程切换原理有什么意义?
- 可以清楚的知道这个线程切换操作会影响到哪些代码的执行线程,不会影响到哪些代码的执行线程
- 灵活运用线程切换来实现复杂的应用场景
- 有利于在发生线程相关的问题时进行调试
不指定线程
举个栗子
由 id 取得图片并显示
由指定的一个 drawable 文件 id drawableRes 取得图片,并显示在 ImageView 中,并在出现异常的时候打印 Toast 报错:
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe() {
@Override
public void call(Subscriber super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
}).subscribe(new Observer() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
RxJava 遵循的是线程不变的原则
在哪个线程调用 subscribe(),就在哪个线程生产事件( onSubscribe.call() )和消费事件(subscriber.onNext())
回顾上一节subscribe()的内部实现,就知道整个调用都是在当前线程直接就调用完成了,没有线程的切换
subscribeOn() 和 observeOn()
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe() {
@Override
public void call(Subscriber super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Observer() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
subscribeOn() 控制事件产生的线程,即onSubscribe.call()执行的线程
observeOn()控制事件消费的线程,即(subscriber.onNext())执行的线程
How?
我们一个一个来说。
subscribeOn()
subscribeOn用的是和lift类似的原理,不过不是直接使用的lift,结合lift的原理,简单来说:
切换线程的时机:
把你调用上一层的observable 的onSubscribe.call()来发送事件的操作放在你指定的Scheduler对应的线程来调用,从而达到切换线程的目的
多个 subscribeOn()
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.map(mapOperator)
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.subscribe(subscriber);
此时 onSubscribe.call()会执行在哪个线程?
当使用了多个 subscribeOn() 的时候,第一个 subscribeOn() ,也就是离最初的Observable最近的那个会决定事件产生的线程。
下面的那些subscribeOn()也会执行线程切换,但是因为最终会被第一个subscribeOn()切换回来,所以并没有起到实质性作用。
多个 subscribeOn()没有任何有意义的使用场景吗?
doOnSubscribe()
多个 subscribeOn()的一个有意义的使用场景
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.subscribe(subscriber);
doOnSubscribe()与onStart()对比
相同点:
都是在subscribe()之后,事件发送之前被调用,用于做一些准备工作不同点:
- onStart()是在subscribe()被调用的线程调用,不能指定线程。所以onStart()不适合做对线程有要求的操作,比如更新UI
- doOnSubscribe() 可以使用subscribeOn()来指定线程。
为什么可以使用subscribeOn()来指定线程
doOnSubscribe跟其他的操作符有点不一样,一般的操作符的操作是在事件发送之后执行的,是对事件的处理,而doOnSubscribe的操作是在subscribe()调用之后从下往上通知的过程中执行的,所以可以用subscribeOn()来指定线程。
observeOn()
observeOn()内部使用的是lift,在它对应的Operator里完成的线程切换。
参考上一节举得Operator的例子
切换线程的时机:在往下一层发送事件的时候切换线程
这里的subscriber指的是observeOn()下一层的subscriber,并不一定是最终的subscriber
多个 observeOn()
Observable.just(1, 2, 3, 4)
.observeOn(Schedulers.newThread())
.map(mapOperator) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator2) // IO 线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // Android 主线程,由 observeOn() 指定
多个observeOn可以多次切换线程,会影响到它下一层的操作的执行线程
多个subscribeOn() 和 observeOn() 混合使用的一个复杂示例
subscribeOn() 和 observeOn() 一般的结合使用示例
一般情况下我们的使用是下面这个样子:
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() 指定
一个subscribeOn来指定事件发送的线程,中间若干个observeOn来指定事件处理和最终事件消费的线程。
参考文献
给 Android 开发者的 RxJava 详解 --扔物线
http://gank.io/post/560e15be2dca930e00da1083