RxJava+Retrofit+OKHttp源码理解

主流网络层的模式是RxJava+Retrofit+OKHttp,所以我开始研究这三个项目的源代码,浅尝理解。
文章的主要内容如下:

1.OkHttp简介
2.OkHttp使用
3.OkHttp流程源码跟踪

一、OKHTTP简介

1.支持HTTP2/SPDY
2.socket自动选择最好路线,并支持自动重连
3.拥有自动维护的socket连接池,减少握手次数
4.拥有队列线程池,轻松写并发
5.拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩)基于Headers的缓存策略

二、OKHTTP使用:

1、GET请求

1529925916(1).jpg

2、POST请求


image.png

三、OKHTTP源码流程分析

(一)、OKHTTP 同步请求debug代码跟踪:


image.png

从上面代码所示,先是new了一个OKHttpClient对象。

1、OKHttpClient类详解
OKHttpClient类就比较简单了:

  • 1、里面包含了很多对象,其实OKhttp的很多功能模块都包装进这个类,让这个类单独提供对外的API,这种外观模式的设计十分的优雅。外观模式。* 2、而内部模块比较多,就使用了Builder模式(建造器模式)。Builder模式(建造器模式)* 3、它的方法只有一个:newCall.返回一个Call对象(一个准备好了的可以执行和取消的请求)。

而大家仔细读源码又会发现构造了OKHttpClient后又new了一个Rquest对象。那么咱们就来看下Request,说道Request又不得不提Response。所以咱们一起讲了

2、Request、Response类详解

Request Response

  • 1、Request、Response分别抽象成请求和相应* 2、其中Request包括Headers和RequestBody,而RequestBody是abstract的,他的子类是有FormBody (表单提交的)和 MultipartBody(文件上传),分别对应了两种不同的MIME类型
    FormBody :"application/x-www-form-urlencoded"
    MultipartBody:"multipart/"+xxx.* 3、其中Response包括Headers和RequestBody,而ResponseBody是abstract的,所以他的子类也是有两个:RealResponseBody和CacheResponseBody,分别代表真实响应和缓存响应。* 4、由于RFC协议规定,所以所有的头部信息不是随便写的,request的header与response的header的标准都不同。具体的见 List of HTTP header fields。OKHttp的封装类Request和Response为了应用程序编程方便,会把一些常用的Header信息专门提取出来,作为局部变量。比如contentType,contentLength,code,message,cacheControl,tag...它们其实都是以name-value对的形势,存储在网络请求的头部信息中。

根据从上面的GET请求,显示用builder构建了Request对象,然后执行了OKHttpClient.java的newCall方法,那么咱们就看看这个newCall里面都做什么操作?


image.png

Call是个什么东西,那咱们看下Call这个类

Call: HTTP请求任务封装
可以说我们能用到的操纵基本上都定义在这个接口里面了,所以也可以说这个类是OKHttp类的核心类了。我们可以通过Call对象来操作请求了。而Call接口内部提供了Factory工厂方法模式(将对象的创建延迟到工厂类的子类去进行,从而实现动态配置)
Call接口提供了内部接口Factory(用于将对象的创建延迟到该工厂类的子类中进行,从而实现动态的配置).

image.png

在源码中,OKHttpClient实现了Call.Factory接口,返回了一个RealCall对象。那我们就来看下RealCall这个类

4、RealCall类详解
RealCall

1、OkHttpClient的newCall方法里面new了RealCall的对象,但是RealCall的构造函数需要传入一个OKHttpClient对象和Request对象(PS:第三个参数false表示不是webSokcet).因此RealCall包装了Request对象。所以RealCall可以很方便地使用这两个对象。
2、RealCall里面的两个关键方法是:execute 和 enqueue。分别用于同步和异步得执行网络请求。
3、RealCall还有一个重要方法是:getResponseWithInterceptorChain,添加拦截器,通过拦截器可以将一个流式工作分解为可配置的分段流程,既增加了灵活性也实现了解耦,关键还可以自有配置,非常完美。

所以client.newCall(request).execute();实际上执行的是RealCall的execute方法,现在咱们再回来看下RealCall的execute的具体实现

image.png

首先是


image.png

判断call是否执行过,可以看出每个Call对象只能使用一次原则。然后调用了captureCallStackTrace()方法。
RealCall.java


image.png

RealCall的captureCallStackTrace() 又调用了Platform.get().getStackTraceForCloseable()
image.png

其实是调用AndroidPlatform. getStackTraceForCloseable(String closer)方法。这里就不详细说了,后面详细说。
然后retryAndFollowUpInterceptor.setCallStackTrace(),在这个方法里面什么都没做就是set一个object进去

image.png

综上所示captureCallStackTrace()这个方法其实是捕获了这个请求的StackTrace。
然后进入了第一个核心类---Dispatcher的的execute方法了,由于下面是进入了关键部分,所以重点讲解下,代码如何:


image.png

看下OKHttpClient的dispatcher()方法的具体内容如下图


image.png

大家发现client.dispatcher()返回的是Dispatcher对象,那么这个Dispatcher对象是何时创建的那?在OkHttpClient.java里面Build类里面的构造函数里面,如下图
image.png

所以默认执行Builder()放到时候就创建了一个Dispatcher。那么咱们看下dispatcher里面的execute()是如何处理的
image.png

里面发现是runningSyncCalls执行了add方法莫非runningSyncCalls是个list,咱们查看dispatcher里面怎么定义runningSyncCalls的。


image.png

原来runningSyncCalls是双向队列啊,突然发现Dispatcher里面定义了三个双向队列,看下注释,我们大概能明白readyAsyncCalls 是一个存放了等待执行任务Call的双向队列,runningAsyncCalls是一个存放异步请求任务Call的双向任务队列,runningSyncCalls是一个存放同步请求的双向队列。关于队列咱们在下篇文章里面详细介绍。

执行完client.dispatcher().executed(this);要走到getResponseWithInterceptorChain();方法了里面了,看下这个方法是具体做什么的?


image.png

发现 new了一个ArrayList,然后就是不断的add,后面 new了 RealInterceptorChain对象,最后调用了chain.proceed()方法。先看下RealInterceptorChain的构造函数。


image.png

发现什么都没做就是做了赋值操作,后面跟踪下chain.proceed()方法
由于Interceptor是个接口,所以应该是具体实现类RealInterceptorChain的proceed实现
image.png

image.png

由于在构造RealInterceptorChain对象时候httpCodec直接赋予了null,所以下面代码直接略过。


image.png

然后看到在proceed方面里面又new了一个RealInterceptorChain类的next对象,温馨提示下,里面的streamAllocation, httpCodec, connection都是null,所以这个next对象和chain最大的区别就是index属性值不同chain是0.而next是1,然后取interceptors下标为1的对象的interceptor。由从上文可知,如果没有开发者自定义的Interceptor时,首先调用的RetryAndFollowUpInterceptor,如果有开发者自己定义的interceptor则调用开发者interceptor。

这里重点说一下,由于后面的interceptor比较多,且涉及的也是重要的部分,而咱们这里主要是讲流程,所以这里就不详细和大家说了,由后面再详细讲解,后面的流程是在每一个interceptor的intercept方法里面都会调用chain.proceed()从而调用下一个interceptor的intercept(next)方法,这样就可以实现遍历getResponseWithInterceptorChain里面interceptors的item,实现遍历循环,缩减后的代码如下:


image.png

image.png

image.png

image.png

读过源码我们知道getResponseWithInterceptorChain里面interceptors的最后一个item是CallServerInterceptor.java,最后一个Interceptor(即CallServerInterceptor)里面是直接返回了response 而不是进行继续递归,具体里面是通过OKio实现的,具体代码,等后面再详细说明,CallServerInterceptor返回response后返回给上一个interceptor,一般是开发者自己定义的networkInterceptor,然后开发者自己的networkInterceptor把他的response返回给前一个interceptor,依次以此类推返回给第一个interceptor,这时候又回到了realCall里面的execute()里面了,代码如下:

image.png

(二)、OKHTTP 异步请求debug代码跟踪:


image.png

前面和同步一样new了一个OKHttp和Request。这块和同步一样就不说了,那么说说和同步不一样的地方,后面异步进入enqueue()方法


image.png

由于executed默认为false,所以先进行判断是否为true,为true则直接跑异常,没有则设置为true,可以看出executed这个是一个标志,标志这个请求是否已经正在请求中,合同步一样先调用了captureCallStackTrace();然后调用 client.dispatcher().enqueue(new AsyncCall(responseCallback));client.dispatcher()返回的是Dispatcher对象所以实际调用的是Dispatcher的enqueue(),那么咱们进入源码看下
image.png

根据源码和注释大家可以看到如果正在执行的异步请求小于64,并且请求同一个主机小于5的时候就先往正在运行的队列里面添加这个call,然后用线程池去执行这个call,否则就把他放到等待队列里面。执行这个call的时候,自然会去走到这个call的run方法,那么咱们看下AsyncCall.java这个类,而AsyncCall.java又继承自NamedRunnable.java咱们就一起看下他们的源码

image.png

image.png

上面看到NamedRunnable的构造方法设置了name在的run方法里面设定为当前线程的name,而NamedRunnable的run方法里面调用了它自己的抽象方法execute,由此可见NamedRunnable的作用就是设置了线程的name,然后回调子类的execute方法,那么我们来看下AsyncCall的execute方法。貌似好像又回到了之前同步的getResponseWithInterceptorChain()里面,根据返回的response来这只callback回调。所以我们得到了OKHTTP的大体流程,如下图:

三、OKHTTP类详解
整体的流程图


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容