美文网首页RX+RetrofitRxjava RxJava
RxJava 系列 (四)RxJava 线程切换原理

RxJava 系列 (四)RxJava 线程切换原理

作者: 嘎啦果安卓兽 | 来源:发表于2017-11-21 17:38 被阅读116次

    前言

    理解线程切换原理有什么意义?

    • 可以清楚的知道这个线程切换操作会影响到哪些代码的执行线程,不会影响到哪些代码的执行线程
    • 灵活运用线程切换来实现复杂的应用场景
    • 有利于在发生线程相关的问题时进行调试

    不指定线程

    举个栗子

    由 id 取得图片并显示
    由指定的一个 drawable 文件 id drawableRes 取得图片,并显示在 ImageView 中,并在出现异常的时候打印 Toast 报错:

    int drawableRes = ...;
    ImageView imageView = ...;
    Observable.create(new OnSubscribe<Drawable>() {
        @Override
        public void call(Subscriber<? super Drawable> subscriber) {
            Drawable drawable = getTheme().getDrawable(drawableRes));
            subscriber.onNext(drawable);
            subscriber.onCompleted();
        }
    }).subscribe(new Observer<Drawable>() {
        @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<Drawable>() {
        @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<Drawable>() {
        @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()之后,事件发送之前被调用,用于做一些准备工作

    • 不同点:

    1. onStart()是在subscribe()被调用的线程调用,不能指定线程。所以onStart()不适合做对线程有要求的操作,比如更新UI
    2. 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

    相关文章

      网友评论

        本文标题:RxJava 系列 (四)RxJava 线程切换原理

        本文链接:https://www.haomeiwen.com/subject/jovwvxtx.html