关于RxJava如今是熟到发紫了,所以对于它底层的动作机制的了解是迫在眉睫了,费话不多说,直接开始。

这里还是以之前获取个人github仓库列表为例,用retrofit+rxjava,也是实际项目中用得最多的,先来回顾一下当时【https://www.cnblogs.com/webor2006/p/10502230.html】在研究retrofit时所定义的API:

这里新建一个工程,深入之前先来学会RxJava的基本使用,先来声明API,首先得增加retrofit的支持,如下:

然后API定义如下:

具体使用:

运行:

以上不多说,只是回顾一下retrofit的用法,接下来改用RxJava的方式,先增加相关的依赖:

,首先API返回的由Call就得变为Single了,如下:

而在我的公司项目中貌似返回的Observable这个对像,如下:

关于这俩的区别在之后再说,先默认使用Single,修改了API之后,接下来使用一下:

此时编译运行一下:

报错了,貌似是无法从Call转换成Single对像,因为API如今是返回Single了:

所以,此时需要增加一个CallAdapter的配置,用来支持进行转换,如下:

然后再运行:

失败了。。这是为啥呢?我们知道其实RxJava是需要手动来切换线程的,所以先增加它,先不管其内部实现:

成功啦,目前由于在回调中木有操作View,如果在回调中操作View会怎么样呢,试试:

再编译运行:

很显然是由于是非UI线程更新View异常了,所以此时对于回调所在线程是在非UI线程里的,所以需要将其切换到主线程里,这里需要增加另外一个库了:

编译运行:

其中还有一个回调方法:

请求前的回调,所以可以在请求前来在界面中增加一个请求提示,如下:

其中这个回调有一个Disposable参数,它的意义简单说可以理解成取消订阅,具体可以这样用它:

好,对于利用Rxjava来实现网络请求的简单使用就到这,接下来则重点是来理解这个框架,当然不是先来直接分析这个代码,而是将其拆解,从最基础的使用上来理解,如下:

Single和Observable:

运行一下:

顺间就显示了,因为是本地的数据,对于纯小白来说看到这样的写法还是挺难理解的,其订阅关系是:

接下来就彻底来挼清这个简单代码的动作机理,先来看下它:

很明显是来对参数做判空,简单瞅下它的实现,很简单:

继续:

这句涉及到两个陌生的东东:SingleJust、RxJavaPlugins.onAssembly(),下面先来看下RxJavaPlugins.onAssembly()的细节:

看到泛型就晕。。也只能硬着头皮细细揣摩:

其中Function是传一个类型的类,返回另一个类型,熟悉Java8的亲们应该比较熟悉:

如果onSingleAssembly不为空则就执行转换了,而它是用户可以配置的,其实也就是是否要对Single对象进行进一步加工,默认肯定是不需要,所以,对于这句话可以简单理解:

所以此just()方法重点看到它就成了,所以点进去看一下SingleJust,如下:

那再看一下Single,发现是一个3000多行超级大的抽象类:

所以里面的细节就不必现跟了,总之记着SingleJust里面重写了subscribeActual()方法:

这个方法会在调用它时最终被调用的:

所以点击进去看是不是这样:

很显然最终会调用SingleJust中的subscribeActual()方法嘛,所以要理解这个回调是如何调用的:

就需要看SingleJust的subscribeActual()方法的具体实现细节啦,如下:

秒懂不,直接就调用了我们传的回调对象嘛,以上简单的示例虽说简单,但是完整的阐述了RxJava的一个订阅实现细节,也就是其比较核心的原理,很重要,再简单挼下:

此时会在里面生成一个SingleJust对像:

当调用它时,实际会调用SingleJust.subscribeActual()方法,而subscribeActual()方法最终又会回调我们的这个对象的回调方法,如下:

至此,整个这个示例原理就彻底理清楚了,接下来继续深入RxJava,此时就涉及到操作符相关的东东了,关于什么是操作符,对于熟悉Java8的亲们也是不会陌生滴,就不过多解释啦,这里以map转换符为例,如下:

此时运行效果一样,这里的map做了啥事件,就已经牵扯到RxJava的结构啦,下面用图来剖析下:

而在我们调用Single.subscribe()方法时,其调用其实是这样子滴:

好,那此时如果再加上一个map的转换操作符后,具体又是如何运转的呢?

具体是如何转换的呢?此时从图中切回到代码:

等于创建了一个新的SingleMap,也就是表象上看着还是Single对象,其实内部已经发生对象的转换了,当然需要将当前的Single对像给传进来:

而此时又回到图画一下当map()之后的形态变化:

接下来就瞅一下SingleMap干了啥?

其中source则是原Single对像,调用了它的subscribe方法,但是!!此时的SingleObserver对像变化了,变为了MapSingleObserver啦,此时的图片形态就变成了:

接下来则具体看下它里面是如何工作的:

接着执行onSuccess()方法:

当然还可以再多增加其它的操作符,总的来说源和目标肯定只有一个,中间的都是形成了一个链,发送会往源传,得到结果之后则从源往目标传,理解这个理念非常之重要。

说了Single,也谈一下Observable,Rxjava1时还木有Single,其实Single就是Observable的简化版本,它的回调就只观注开始和结束,而对于Observable而言,它的中间过程也会回调,如下:

其中的onNext()可能会被调用多次,了解一下区别既可。

Disposable:

在之前咱们已经稍微提到过它,其实是可以做取消订阅用的,下面用一个例子来实践一下,这里采用Observable来进行每秒间隔发事件,如下:

运行看一下效果:

此时增加一个按钮,在运行期间将其停止掉,这时就可以利用Disposable了,如下:

运行:

接下来很想知道Observable.subscribe()方法到底做了啥导致在onSubscribe()方法中得到了一个什么样的Disposable,才能知道Disposable是怎么工作的,不同的Observable它的Disposable是不一样的,所以咱们先来看一下目前这个Observable是一个什么样的Observable,看它:

点开看一下interval():

下面来细看一下:

此时就得查看一下IntervalObserver了:

也就是说:

调用的也就是IntervalObserver.dispose()方法,所以得研究一下它的具体实现:

然后看一下具体实现:

而对于IntervalObserver中的run()方法就会根据这个状态来做判断,如下:

没有中间操作符的Disposable工作方式都是这样的,没有一个传递的过程,接下来咱们来看一下有map操作的Disposable又是咋样的:

然后点击查看源码:

也就是最终:

调用的是MapObserver.dispose()方法,但是!!貌似在MapObserver中木有找到dispose()方法呀:

这就需要往父类进行查找喽,果真有,如下:

其中的s是?

那不就是源,也就是最终会调用源的dispose()方法,如下:

总结Disposable也就是两种场景:

1、原始的Disposable则是停掉自己的任务。

2、如果带有操作符的,则先停掉自己,然后再停掉原始的Disposable。

线程切换Scheduler:

关于线程切换有几处,咱们一个个来剖析,这里还是回到Single的程序来,首先来瞅下它:

点击进行:

其中核心是:

也就是我们调用subscribeOn所传的参数:

点进去瞅下:

具体它的方法先不分析,可以看到参数是需要一个Runnable,而由于SubscribeOnObserver实现了Runnable方法,所以当然可以将它传进去喽:

而它里面的run方法的实现:

也就是说:

此时我们的调用就会在指定的线程来做了,如下:

用图来说明一下这个subscribeOn()的过程:

那如果写多个subscribeOn()呢?

其实最终会用第一行的切线程,图例表示其形态:

而对于subscribeOn()之后的Disposable则为:

也就是subscribeOn()指定的线程是用来决定subscribe()的订阅操作的,那接下来看一下observeOn(),如下:

此时就看一下它的回调中瞅下是否有切线程的东东:

那如果有多个observeOn会有啥结果呢?由于是管下面的,可能会是这样:

那么就有东东可以利用啦,怎么利用?假如再插入一个操作符:

也就是使用observeOn()多次切是会生效的,而不像subscribeOn()使用多次是没啥效果的,那有了observeOn()的灵活性,subscribeOn()是不是可以不用了?当然不行,因为subscribeOn()是管订阅的,需要配合着使用。总的来说:subscribeOn()是先切线程再进行订阅,而observeOn()是先订阅,而每次回调会切线程处理

接下来再研究一下它们的原理:

首先来瞅一下Schedulers.io(),不过有一个跟它类似的api需要先看它:

先说一下它们俩的区别:

Schedulers.newThread():每次都会新开一个线程。

Schedulers.io():里面用到了线程池,不是每次都新开线程。

好,先来了解下Schedulers.newThread()是做了啥?

直接看NEW_THREAD:

而切换线程在上面的分析中主要是这句代码:

所以咱们来瞅一下它里面有木有scheduleDirect()这个方法,发现木有。。那就肯定是在它的父类找呗,找找:

确实是有:

下面来简单分析一下:

然后核心代码:

点进去瞅一下:

抽象方法,肯定得看子类:

一层套一层,继续:

其实可以看出Scheduler就是包装Worker的东东,而Worker是包装了executor的东东,咱们再回过头来看:

就会调到子类:

再回过头来瞅一下DisposeTask:

好,了解了Schedulers.newThread()机制之后, 就可以来理解Schedulers.io()了,其实会有一个IoScheduler对像,如下:

然后它里面也有一个createWork()方法,瞅一下:

就不往里跟了,很明显确实io()就是带有缓存的,不是每次都new一个线程。

好接下来就看最后一个主线程的scheduler啦:

点击进来:

然后可以大致看一下切换细节:

至此!!关于Rxjava的核心机制原理都已经剖析完毕,还是挺麻烦!!

RxJava 以及 Android 中的通用线程解决方案、并发与线程安全的更多相关文章

  1. RxJava在Android中使用场景详解

    RxJava 系列文章 <一,RxJava create操作符的用法和源码分析> <二,RxJava map操作符用法详解> <三,RxJava flatMap操作符用法 ...

  2. Android中使用ListView实现分页刷新(线程休眠模拟)

    当要显示的数据过多时,为了更好的提升用户感知,在很多APP中都会使用分页刷新显示,比如浏览新闻,向下滑动到当前ListView的最后一条信息(item)时,会提示刷新加载,然后加载更新后的内容.此过程 ...

  3. Android中使用ListView实现分页刷新(线程休眠模拟)(滑动加载列表)

    当要显示的数据过多时,为了更好的提升用户感知,在很多APP中都会使用分页刷新显示,比如浏览新闻,向下滑动到当前ListView的最后一条信息(item)时,会提示刷新加载,然后加载更新后的内容.此过程 ...

  4. android中ViewHolder通用简洁写法

    public class ViewHolder {     // I added a generic return type to reduce the casting noise in client ...

  5. Android中UI线程与后台线程交互设计的5种方法

    我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必 ...

  6. Android中线程和线程池

    我们知道线程是CPU调度的最小单位.在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的.在Android中,除了Thread外,扮演线程的角色有很多,如AsyncTask,Inte ...

  7. Android中关于Handler的若干思考

    在之前的博文中,讲过一些和Handler有关的知识,例如: Android 多线程----AsyncTask异步任务详解 Android多线程----异步消息处理机制之Handler详解 今天再把Ha ...

  8. 在Android中使用并发来提高速度和性能

    Android框架提供了很实用的异步处理类.然而它们中的大多数在一个单一的后台线程中排队.当你需要多个线程时你是怎么做的? 众所周知,UI更新发生在UI线程(也称为主线程).在主线程中的任何操作都会阻 ...

  9. 系统剖析Android中的内存泄漏

    [转发]作为Android开发人员,我们或多或少都听说过内存泄漏.那么何为内存泄漏,Android中的内存泄漏又是什么样子的呢,本文将简单概括的进行一些总结. 关于内存泄露的定义,我可以理解成这样 没 ...

随机推荐

  1. 18点睛Spring4.1-Meta Annotation

    18.1 Meta Annotation 元注解:顾名思义,就是注解的注解 当我们某几个注解要在多个地方重复使用的时候,写起来比较麻烦,定义一个元注解可以包含多个注解的含义,从而简化代码 下面我们用& ...

  2. iOS-关于创建真机调试证书(发布证书,测试证书,推送调试证书)【转】

  3. 进程间通信之pipe

    实现数据传递 两个进程之间通信 多个进程之间通信,会导致数据不安全,需要加锁,示例 分类 无名管道:父子间进程通信 有名管道:父子间进程通信:任意两个进程之间通信 创建管道方法 os.mkfifo(p ...

  4. Mathtype安装与最简破解

    1.MathType资源 链接: https://pan.baidu.com/s/1UapJCcfU7Me_rIWdAe5nfw   提取码:   1y9i 2.破解 我之前的30天试用期过了,没来得 ...

  5. Linux_高级用法

    LInux如何压缩和解压文件 文件压缩与解压主要讲zip和tar 安静模式和文件夹 zip -r -q -o test.zip 需要打包文件 查看打包文件 du -h test.zip 上节学过的fi ...

  6. spark kmer计算

    输入文件:fa格式的文件 输出结果:kmer的频数和对应的kmer类型和计数 1.将fq.gz的文件转换成fa文件: #!/usr/bin/python env # -*- coding:utf-8 ...

  7. CentOS7.6安装Pycharm并添加快捷方式

    1.以用户身份登录jiangshan 并建立/home/jiangshan/pycharm文件夹2.下载 pycharm-community-anaconda-2019.1.3.tar.gz 放置在/ ...

  8. [转帖]负载均衡 LVS+Keepalived

    负载均衡 LVS+Keepalived https://www.cnblogs.com/jicki/p/5546862.html 改天试试 一直没搞过. 系统  CentOS 5.6 X64 物理机 ...

  9. Springmvc在项目启动时查询数据库并初始化静态变量

    private static List<ResourceEntity> resourceList = null; //初始化的全局静态变量 @Autowired private Resou ...

  10. python学习-40 生产者和消费者模型

    import time def buy(name): # 消费者 print('%s上街去买蛋' %name) while True: eggs=yield print('%s买了%s' %(name ...