Rxjava这么强大的类库怎么可能没有多线程切换呢?

其中observeOn()与subscribeOn()就是实现这样的作用的。本文主要讲解observeOn()与subscribeOn()的用法,不去探究其中的原理。

0. 默认情况

在默认情况下,其不做任何线程处理,Observable和Observer处于同一线程,没有做任何线程切换,依次执行,如下图所示:

可以写一个demo测试之:

Observable<String> source = Observable.just("Alpha","Beta","Gamma");
        source.subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onNext(Integer integer) {
                    Log.i("TAG", "Received " + integer + " on thread:" +
                    Thread.currentThread().getName());
            }
        });

1. subscribeOn()的作用

该方法是指明数据产生的线程,即Observable发射数据所在的线程,如果之后不做任何处理,操作符operator(如map,flatmap等)也在subscribeOn指定的线程做数据处理。

多次使用subscribeOn()并不能频繁地切换线程,只有距离数据源最近的一个subscribeOn()唯一确定数据源发射数据的线程。如代码所示:

Observable<String> source = Observable.just("Alpha","Beta","Gamma");
source
.subscribeOn(Schedulers.computation())
.subscribeOn(Schedulers.newThread())
.subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onNext(Integer integer) {
                    Log.i("TAG", "onNext: " + "on thread:" + Thread.currentThread().getName());
            }
        });
    }

其中只有subscribeOn(Schedulers.computation())对数据源source起作用,该source在Schedulers.computation()指定的线程发射数据。如果后面没有使用observeOn(),操作符operator都会在Schedulers.computation()所指定的线程做数据变换。

2. observeOn()的作用

在Android开发中,我们经常面临这样的场景,在工作者线程中产生数据,在UI线程中更新相应的View,subscribeOn()指定了数据发射的线程,但我们更新UI的操作,不可能在发射数据的线程运行,这会造成ANR的问题。此时就必须通过observeOn()方法做线程的切换:

Observable<String> source = Observable.just("Alpha","Beta","Gamma");
        source.map(new Func1<String, Integer>() {
            @Override
            public Integer call(String s) {
                Log.i("TAG", "call: " + Thread.currentThread().getName());
                return s.length();
            }
        }).observeOn(Schedulers.newThread()).subscribeOn(Schedulers.computation()).
                subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onNext(Integer integer) {
                    Log.i("TAG", "onNext: " + "on thread:" + Thread.currentThread().getName());
            }
        });

上述代码的运行结果如下:

TAG: call: RxComputationScheduler-1
TAG: onNext: on thread:RxNewThreadScheduler-1
TAG: call: RxComputationScheduler-1
TAG: onNext: on thread:RxNewThreadScheduler-1
TAG: call: RxComputationScheduler-1
TAG: onNext: on thread:RxNewThreadScheduler-1

可以看到,在observeOn()之前的操作,都运行在subscribeOn(Schedulers.computation())指定的线程,即RxComputationScheduler-1线程;而使用了observeOn()之后,在它之后的操作都

运行在了observeOn(Schedulers.newThread())指定的线程。

所以,给出一个结论observeOn()只对其之后的操作起作用;observeOn()可以使用多次,每次使用对其之后的operator起作用,对之前的操作没有影响。

上图很好地诠释了ObserveOn的作用。

3. backpressure的问题

由于ObserveOn的作用,数据流在多个线程中不断的传输,可能存在速度不匹配的情况。如下图所示,当底部的数据流发射速度快于顶部数据流的处理速度,若产生异常,可能导致一部分数据未被顶部的subscriber处理。

废话太多,说不清楚,看下代码吧:

Observable<String> source = Observable.just("Alpha","Beta","Gamma");
        source.map(new Func1<String, Integer>() {
            @Override
            public Integer call(String s) {
                Log.i("TAG", "call: " + Thread.currentThread().getName());
                if (s.equals("Gamma"))
                    throw new RuntimeException();
                return s.length();
            }
        })
         .doOnError(new Action1<Throwable>() {
             @Override
             public void call(Throwable throwable) {
                  Log.i("TAG", "doOnError: " + Thread.currentThread().getName());
             }
         })
        .observeOn(Schedulers.newThread())
        .subscribeOn(Schedulers.computation())
        .subscribe(new Subscriber<Integer>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                Log.i("TAG", "onError: " + "on thread:" + Thread.currentThread().getName());
            }

            @Override
            public void onNext(Integer integer) {
                    Log.i("TAG", "onNext: " + "on thread:" + Thread.currentThread().getName());
            }
        });
    }

看一下运行结果:

TAG: call: RxComputationScheduler-1
TAG: call: RxComputationScheduler-1
TAG: call: RxComputationScheduler-1
TAG: doOnError: RxComputationScheduler-1
TAG: onError: on thread:RxNewThreadScheduler-1

我们就可以看到,当发射的数据为Gamma时抛出异常,之前发射的数据"Alpha","Beta"还未被subsriber的onNext方法处理,这就是backpressure问题。

4. onErrorResumeNext

onErrorResumeNext是错误恢复处理方法,当我们数据链中某个操作符抛出异常,此时会中断整个数据链,但我们想尝试恢复一下,这时可以使用

onErrorResumeNext。比如Android在过于频繁登录时,系统会弹出一个dialog(弹窗),让用户输入验证码,该逻辑就可以放在onErrorResumeNext中处理。

我们先看一段代码:

Observable<String> source = Observable.just("Alpha","Beta","Gamma");
        source.map(new Func1<String, Integer>() {
            @Override
            public Integer call(String s) {
                Log.i("TAG", "call: " + Thread.currentThread().getName());
                if (s.equals("Gamma"))
                    throw new RuntimeException();
                return s.length();
            }
        })
                .observeOn(Schedulers.newThread())
                .subscribeOn(Schedulers.computation())
                .onErrorResumeNext(new Func1<Throwable, Observable<? extends Integer>>() {
            @Override
            public Observable<? extends Integer> call(Throwable throwable) {
                return Observable.just(1000).map(new Func1<Integer, Integer>() {
                    @Override
                    public Integer call(Integer integer) {
                        Log.i("TAG", "call:onErrorResumeNext: "  +  Thread.currentThread().getName());
                        return integer;
                    }
                })
                .subscribeOn(Schedulers.computation());
            }
        })
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.i("TAG", "onError: " + "on thread:" + Thread.currentThread().getName());
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.i("TAG", "onNext: " + "on thread:" + Thread.currentThread().getName());
                    }
                });

看上段代码中标红的部分,为什么在onErrorResumeNext可以再次使用subscribeOn(),我的猜测(并没有看源码)可能是该段代码产生了新的数据源,所以可以使用subsribeOn()指定数据源发射数据的线程。

它的运行结果:

TAG: call: RxComputationScheduler-1
TAG: call: RxComputationScheduler-1
TAG: call: RxComputationScheduler-1
TAG: onNext: on thread:RxNewThreadScheduler-1
TAG: onNext: on thread:RxNewThreadScheduler-1
TAG: call:onErrorResumeNext: RxComputationScheduler-2
TAG: onNext: on thread:RxComputationScheduler-2

看运行结果在onErrorResumeNext方法中使用了subscribeOn(),线程切换到了RxComputationScheduler-2,在之后没有observeOn的情况下,最后一个onNext也运行在了RxComputationScheduler-2。很神奇!!!!!

Rxjava observeOn()和subscribeOn()初探的更多相关文章

  1. RxJava尝试取代Handler初探

    在之前的一篇文章中,我们探究了RxJava的使用方法,详细请看https://www.cnblogs.com/yanyojun/p/9745675.html 根据扔物线大神的描述,如果用一个词来概括R ...

  2. 78. Android之 RxJava 详解

    转载:http://gank.io/post/560e15be2dca930e00da1083 前言 我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Fli ...

  3. 给 Android 开发者的 RxJava 详解

    我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个 ...

  4. 一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库 RxJava,相当好

    https://github.com/ReactiveX/RxJava https://github.com/ReactiveX/RxAndroid RX (Reactive Extensions,响 ...

  5. RxJava开发精要7 – Schedulers-解决Android主线程问题

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...

  6. RxJava系列6(从微观角度解读RxJava源码)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  7. RxJava(11-线程调度Scheduler)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51821940 本文出自:[openXu的博客] 目录: 使用示例 subscribeOn原理 ...

  8. 理解 RxJava 的线程模型

    来源:鸟窝, colobu.com/2016/07/25/understanding-rxjava-thread-model/ 如有好文章投稿,请点击 → 这里了解详情 ReactiveX是React ...

  9. android ------- 开发者的 RxJava 详解

    在正文开始之前的最后,放上 GitHub 链接和引入依赖的 gradle 代码: Github: https://github.com/ReactiveX/RxJava https://github. ...

随机推荐

  1. [Python]peewee使用经验

    peewee 使用经验 本文使用案例是基于 python2.7 实现 以下内容均为个人使用 peewee 的经验和遇到的坑,不会涉及过多的基本操作.所以,没有使用过 peewee,可以先阅读文档 正确 ...

  2. React-Native 之 项目实战(五)

    前言 本文 有配套视频,可以酌情观看. 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我讨论. 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关,如文中内 ...

  3. php面向对象(一)---2017-04-17

    重点:定义类与实例化类:访问修饰符:构造函数 一.面向对象 面向对于与面向过程的主要区别在于:前者包含类和对象的概念 二.类和对象   1.类是由众多对象抽象(归纳总结)出来的东西  代表所有对象的特 ...

  4. Java Script 字符串操作

    JS中常用几种字符串操作: big() small() bold() fontcolor() fontsize() italics() strike() link() charAt() charCod ...

  5. PHP 魔术方法 __call 与 __callStatic 方法

    PHP 魔术方法 __call 与 __callStatic 方法 PHP 5.3 后新增了 __call 与 __callStatic 魔法方法. __call 当要调用的方法不存在或权限不足时,会 ...

  6. SIP DB33标准笔记 注册/目录发送/心跳

    SIP协议扩展中: 在 RFC 3261 基础上定义了一个新方法 DO.方法 DO 的功能包括:控制对方动作.更新对方信息.查询对方状态.历史监控资料查询和回放等.发送方法 DO 的请求报文时,不会创 ...

  7. linux下安装node

    经过一番的折腾终于在linux上安装了node,记录下来以免忘记 1.下载node 去官网下载最新的linux版本下对应node.js,node-v6.10.2-linux-x64.tar.gz 2. ...

  8. MongoDB基础教程系列--第九篇 MongoDB 分片

    1.分片介绍 分片(sharding)是将数据拆分,将其分散存到不同机器上的过程.MongoDB 支持自动分片,可以使数据库架构对应用程序不可见.对于应用程序来说,好像始终在使用一个单机的 Mongo ...

  9. 进程间通信系列 之 socket套接字及其实例

    进程间通信系列 之 概述与对比   http://blog.csdn.net/younger_china/article/details/15808685  进程间通信系列 之 共享内存及其实例   ...

  10. 纯净CentOS7.2 yum源配置与使用yum 安装系统工具net-tools

    本节我们来讲CentOS 的yum 源配置 一.yum 简介 yum,是Yellow dog Updater, Modified 的简称,是杜克大学为了提高RPM 软件包安装性而开发的一种软件包管理器 ...