RxJava 以及 Android 中的通用线程解决方案、并发与线程安全
关于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 中的通用线程解决方案、并发与线程安全的更多相关文章
- RxJava在Android中使用场景详解
RxJava 系列文章 <一,RxJava create操作符的用法和源码分析> <二,RxJava map操作符用法详解> <三,RxJava flatMap操作符用法 ...
- Android中使用ListView实现分页刷新(线程休眠模拟)
当要显示的数据过多时,为了更好的提升用户感知,在很多APP中都会使用分页刷新显示,比如浏览新闻,向下滑动到当前ListView的最后一条信息(item)时,会提示刷新加载,然后加载更新后的内容.此过程 ...
- Android中使用ListView实现分页刷新(线程休眠模拟)(滑动加载列表)
当要显示的数据过多时,为了更好的提升用户感知,在很多APP中都会使用分页刷新显示,比如浏览新闻,向下滑动到当前ListView的最后一条信息(item)时,会提示刷新加载,然后加载更新后的内容.此过程 ...
- android中ViewHolder通用简洁写法
public class ViewHolder { // I added a generic return type to reduce the casting noise in client ...
- Android中UI线程与后台线程交互设计的5种方法
我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一 些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必 ...
- Android中线程和线程池
我们知道线程是CPU调度的最小单位.在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的.在Android中,除了Thread外,扮演线程的角色有很多,如AsyncTask,Inte ...
- Android中关于Handler的若干思考
在之前的博文中,讲过一些和Handler有关的知识,例如: Android 多线程----AsyncTask异步任务详解 Android多线程----异步消息处理机制之Handler详解 今天再把Ha ...
- 在Android中使用并发来提高速度和性能
Android框架提供了很实用的异步处理类.然而它们中的大多数在一个单一的后台线程中排队.当你需要多个线程时你是怎么做的? 众所周知,UI更新发生在UI线程(也称为主线程).在主线程中的任何操作都会阻 ...
- 系统剖析Android中的内存泄漏
[转发]作为Android开发人员,我们或多或少都听说过内存泄漏.那么何为内存泄漏,Android中的内存泄漏又是什么样子的呢,本文将简单概括的进行一些总结. 关于内存泄露的定义,我可以理解成这样 没 ...
随机推荐
- c# .net 查找并安装CA根证书
https CA根证书 用的是证书指纹来查找. 在用 collection.Find 之前 ,X509Store 一定要打开(Open),否则找到的数量(X509Certificate2Collect ...
- kubernetes的几个概念
1. rc: 副本控制器,确保在任何时候都运行指定数量的pod副本.换句话说,ReplicationController确保一个pod或一组同构的pod始终处于可用状态. 2. rs:副本集,是rc ...
- 【计算机视觉】行为识别(action recognition)相关资料
================华丽分割线=================这部分来自知乎==================== 链接:http://www.zhihu.com/question/3 ...
- shell学习及脚步编写
目录: shell基础变量逻辑运算符 scp基础用法脚本 while+for+case基础用法脚本 内核优化脚本 自动修改本机ip脚本 for+case 查询日志脚本 一键yum安装lamp脚本 源码 ...
- Beta冲刺(4/4)
队名:秃头小队 组长博客 作业博客 组长徐俊杰 过去两天完成的任务:学习了很多东西 Github签入记录 接下来的计划:继续学习 还剩下哪些任务:细节处理 燃尽图 遇到的困难:自己太菜了 收获和疑问: ...
- 遨游TypeScript海洋之定义变量和数据类型
变量和数据类型 熟悉JavaScript的小伙伴都知道,typescript是JavaScript的超集,也就是说它包含JavaScript.所以我觉得,只要你想拥有更佳的模块管理,让你的开发更佳严谨 ...
- 修改Linux服务器中的MySql密码
1.可以直接在数据库中修改,因为知道root密码,所以直接登录 mysql -uroot -p 2.查看一下数据库,修改root密码需要使用如下图所示的mysql数据库 3.通过use mysql指明 ...
- Duration和Period的区别--通俗易懂
在jdk1.8以后,对表示日期时间的类型进行了重新分类,这里出现了2个新的类,Duraction 和Period Duraction表示:时间的区间,用来度量秒和纳秒之间的时间值 Period表示:一 ...
- 《Mysql - Order By 的工作原理?》
一:概述 - order by 用于 SQL 语句中的排序. - 以 select city,name,age from t where city='杭州' order by name limit ...
- idea使用maven+Tomcat
1.创建maven项目,并使用webapp骨架,并修改pom.xml文件 <build> <finalName>myWebApp</finalName> <! ...