Okhttp3-网络请求流程解析

前言

           已经大火2年的Retrofit,必然会提到另外两个库,OKhttp3和Rxjava,尤其前者,作为Retrofic网络请求的底层库,我们有必要了解OKhttp3的网络请求是如何运作的,就会理解为什么OKhttp3比其它网络请求库更高效,为什么Volley,Glide,Picasso在后续版本纷纷改用或支持OKhttp3作为自身网络传输层的底层库。


基本构成

           OKhttp3作为Square公司(贡献了很多优秀的开源库,比如Retrofit,OKhttp,Okio,Picasso等)开发,旨于替换Java的HttpUrlConnection和Apache的HttpClient的轻量级网络框架,已经被运用到很多开源库以及Android的源码中(Android Studio 在6.0之后,移除了HttpClient,并且用OKHttp代替了HttpUrlConnection)。

和其它网络框架类似,OKhttp3也主要由以下6个概念一起运作:

1.OkHttpClient:客户端;

2.Dispatcher:线程池;

3.Interceptor:拦截器(OKhttp的特色);

4.Request:请求;

5.Response:响应;

6.CallBack:回调。


Get请求流程

Okhttp3-网络请求流程解析_第1张图片
1-get请求

1.创建OkHttpClient客户端.

2.创建Request请求,设置url.

3.通过OkHttpClient的newCall()方法,将Request包装成一个Call接口.

4.最后调用execute()方法得到同步的响应Response.

5.或者调用execute()方法,以CallBack回调接口作为参数,得到异步的响应Response.


POST请求流程

Okhttp3-网络请求流程解析_第2张图片
2-post请求

流程和GET请求类似.

不同的地方在于:

1.需要RequestBody请求体封装各种类型的请求参数.

2.调用Request.Builder的post()方法,传入RequestBody,设置为POST请求.


接下来,我们对其内部的流程进行分析

Get请求内部流程分析

第一步:OkHttpClient client = new OkHttpClient();

1.通过OkHttpClient的Builder的默认构造方法来初始化网络所需的各种成员:

Okhttp3-网络请求流程解析_第3张图片
3-OkHttpClient-Builder

这些成员依次为:

Dispatcher:线程池

Proxy:代理

Protocol:协议

ConnectionSpec:连接规则

Interceptor:拦截器

ProxySelector:代理选择器

CookieJar:Cookie缓存

Cache:缓存

SocketFactory:Socket工厂

HostnameVerifier:主机校队

SSLSocketFactory/CertificatePinner:SSL证书相关

ConnectionPool:连接池

等等

2.然后再由Builder将这些成员设置给OkHttpClient对象:

Okhttp3-网络请求流程解析_第4张图片
4-OkHttpClient



第二步:Request request = new Request.Builder().url("http://xxxxxx").build();

1.通过Request的Builder的默认构造方法,初始化请求部分所需的请求方式method和默认的请求头headers,

Okhttp3-网络请求流程解析_第5张图片
5-Request-Builder

2.再由Builder的url()方法设置请求的链接地址,最后调用build()方法返回Request对象。

Okhttp3-网络请求流程解析_第6张图片
6-Builder-url()-build()

3.在Builder的build()方法中,调用了Request的构造参数,将method,headers等成员设置给Request对象

Okhttp3-网络请求流程解析_第7张图片
6-Request

第三步:Call call =client.newCall(request);

1.通过OkhttpClient的newCall()方法,构建一个RealCall对象,且其持有第一,二步创建的OkhttpClient和Request对象的引用。

7-RealCall

2.同时RealCall对象还会创建一个拦截器RetryAndFollowUpInterceptor。

Okhttp3-网络请求流程解析_第8张图片
8-RealCall


Okhttp3-网络请求流程解析_第9张图片
9-RetryAndFollowUpInterceptor

第四步:

(一)同步请求:Response response =call.execute();

1.调用第三步的RealCall对象的execute()方法,该方法先检查该RealCall对象是否已经执行过该方法了,重复执行会抛出异常。

Okhttp3-网络请求流程解析_第10张图片
10-RealCall-execute()

2.调用第一步的OkhttpClient的dispatcher()方法,获取到OkhttpClient的线程池Dispatcher,然后执行其executed()方法;

runningSyncCalls是Dispatcher中维护的一个正在执行的同步请求队列,RealCall对象会被加入到该队列的末尾。

11-Dispatcher-executed()

3.然后执行RealCall对象的getResponseWithInterceptorChain()方法;

按固定顺序将拦截器(依次为初始化OkhttpClient的拦截器,重试和重定向的拦截器,桥接转换拦截器,缓存拦截器,连接拦截器,初始化OkhttpClient的网络拦截器,服务器回调拦截器)添加到Interceptors集合中,与第二步的Request一起作为构造参数,创建了一个RealInterceptorChain对象。

这里注意构造参数中的(this.index)0,就是RealInterceptorChain的成员变量index,用来标记执行到了Interceptors集合中哪一个拦截器的intercept()方法。

Okhttp3-网络请求流程解析_第11张图片
12-RealCall-getResponseWithInterceptorChain()

4.紧接着执行RealInterceptorChain对象的proceed()方法;

首先会作判断:if(this.index >= this.interceptors.size()) {

throw new AssertionError();

} else { ... },这个判断有什么用呢,先看下面。

因为首次进入httpStream为null,所以不会执行同一请求检查this.sameConnection(request.url())和请求次数的检查this.calls >1.

5.接着看以下三句代码:

RealInterceptorChain next = new RealInterceptorChain(this.interceptors, streamAllocation, httpStream, connection, this.index +1, request);

Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);

Response response = interceptor.intercept(next);

(1)以Interceptors集合和Request对象,以及index+1作为构造参数,创建一个新的RealInterceptorChain对象;

(2)执行index位置的拦截器的intercept()方法,同时将新创建的RealInterceptorChain对象传递进去,很明显,这个RealInterceptorChain对象的proceed()方法又会被执行,因此,结合第4点的代码判断,通过迭代,Interceptors集合中的所有拦截器都会执行intercept()方法:

a.OkhttpClient的interceptors集合,默认是空集;

b.RetryAndFollowUpInterceptor拦截器:负责请求的重试和重定向,最多20次。

c.BridgeInterceptor桥接拦截器:负责请求构建和响应

d.CacheInterceptor缓存拦截器:负责网络缓存操作

e.ConnectInterceptor连接拦截器:负责socket的IO操作,这里使用了Okio提供的封装

Okhttp3-网络请求流程解析_第12张图片
13 ConnectInterceptor-intercept()

socket操作就是在这个拦截器里执行的。

Okhttp3-网络请求流程解析_第13张图片
14-StreamAllocation-newStream()

f.OkhttpClient的networkInterceptors拦截器,默认是空集.

g.CallServerInterceptor拦截器:向服务器发送请求,将请求header和body写入socket中,然后读取响应header和body,返回最后需要的响应数据.

下图是CallServerInterceptor的intercept()方法的实现

Okhttp3-网络请求流程解析_第14张图片
15 CallServerInterceptor-proceed()

(4)最后的CallServerInterceptor拦截器执行完intercept()方法后,返回请求的响应数据:Response对象.

Okhttp3-网络请求流程解析_第15张图片
16 RealInterceptorChain-proceed()

服务器响应的数据主要通过其中的ResponseBody对象获取。

Okhttp3-网络请求流程解析_第16张图片
17 Response

5.最后不管请求是否成功,最后都会执行Dispatcher的finished()方法,结束整个请求;

同步请求,不会执行Dispatcher的promoteCalls()方法(这个方法在后面的异步请求再分析),通过runningSyncCalls队列的remove()方法将RealCall从运行队列中移除.

Okhttp3-网络请求流程解析_第17张图片
18 Dispatcher-finished()



(二)异步请求:call.enqueue(new Callback() {

@Override

    public void onFailure(Call call,IOException e) {}

@Override

    public void onResponse(Call call,Response response) throwsIOException {}

});

与同步请求不同的地方在于

1.执行RealCall对象的的enqueue()方法,需要一个CallBack接口实现作为参数,执行最后请求的成功和失败回调;

该方法内部是调用OkhttpClient的Dispatcher的enqueue()方法,同时传入一个AsyncCall对象作为参数,每个RealCall对象只能执行一次。

Okhttp3-网络请求流程解析_第18张图片
19 ReallCall-enqueue()

2.AsyncCall对象持有第1点中创建的CallBack对象.

Okhttp3-网络请求流程解析_第19张图片
20 AsyncCall

3.如果正在执行的异步队列runningAsyncCalls没有超过最大请求数(最大为64)并且该请求的主机的最大请求数没有超过最大限制(最大为5)时,AsyncCall对象会被加入到runningAsyncCalls中;否则,AsyncCall会被加入到准备执行的异步队列readyAsyncCalls中。

Okhttp3-网络请求流程解析_第20张图片
21 Dispatcher-enqueue()

4.如果AsyncCall加入了运行队列,会通过Dispatcher的executorService()方法,创建一个单例线程池ThreadPoolExecutor

Okhttp3-网络请求流程解析_第21张图片
22 Dispatcher-executorService()

使用到的ThreadPoolExecutor的构造参数:

corePoolSize:并发数,maximumPoolSize:最小线程数为0,最大线程数为Integer.MAX_VALUE;

keepAliveTime:空闲线程的存活时间;

workQueue:先进先出的工作队列;

threadFactory:单个线程的线程工厂。

Okhttp3-网络请求流程解析_第22张图片
23 ThreadPoolExecutor

紧接着调用ThreadPoolExecutorexecute()方法,

command就是传入的AsyncCall,然后执行addWorker()方法

Okhttp3-网络请求流程解析_第23张图片
24 ThreadPoolExecutor-execute()

在addWorker()方法中,可以发现firstTask(即上述的AsyncCall),被包装成Worker后,再由其内部Thread执行了start()方法。

Okhttp3-网络请求流程解析_第24张图片
25 ThreadPoolExecutor-addWorker()1
Okhttp3-网络请求流程解析_第25张图片
26 ThreadPoolExecutor-addWorker()2

5.AsyncCall 继承自NamedRunnable 而NamedRunnable是Runnable接口的抽象实现。

27 AsyncCall
Okhttp3-网络请求流程解析_第26张图片
28 NamedRunnable

ThreadPoolExecutor的execute()方法执行了工作线程,触发了线程内部的Runnable(即AsyncCall )的run()方法,run()方法内部执行AsyncCall的execute()方法。

6.最后执行的AsyncCall的execute()方法

调用RealCall的getResponseWithInterceptorChain()方法获取最后响应的数据Response(这一步的内部流程和同步请求一样,不再累述)。

如果中途通过retryAndFollowUpInterceptor拦截器取消了请求,或者抛出IO异常,则请求失败,回调responseCallBack(即第1点传入的CallBack接口实现)的onFailure()方法;否则,请求成功,回调responseCallBack的onResponse()方法

不管请求失败还是成功,都会调用线程池Dispatcher的finished()方法。

Okhttp3-网络请求流程解析_第27张图片
29 AsyncCall-execute()

异步请求在结束请求时,传入的promoteCalls为true,将改请求从正在执行的异步队列中移除后,会额外执行promoteCalls()方法,检查是否有待执行的请求。

Okhttp3-网络请求流程解析_第28张图片
30 Disptcher-finish()


Okhttp3-网络请求流程解析_第29张图片
31 Dispatcher-finish()

promoteCalls()方法中,如果正在执行的异步队列的请求数小于最大请求数,就会继续检查准备执行的队列中是否有还没有执行的请求,如果有,则取出最早存入准备执行的队列的AsyncCall,只要单个主机的请求数也小于最大请求数,就会重复上述第3点的方法,将这个没执行的请求执行下去。

Okhttp3-网络请求流程解析_第30张图片
31 Dispatcher- promoteCalls()

从上述可以总结这个Dispatcher的特点:

1.Okhttp3的Dispatcher线程池,同步请求为一个工作对列,异步请求时通过一个工作队列和一个准备队列来互相配合,支持最大64个的并发请求,通过Deque队列先进先出的特点控制请求执行的顺序,而不是通过锁机制;

2.整个Dispatcher内部只创建一个ThreadPoolExecutor,不保存最小的存活线程数,最大线程数为Integer.MAX_VALUE,为每个正在执行的请求创建一个线程,当线程空闲60s后,结束线程;

3.设置有主机数限制,最大每个主机支持5个请求。

在异步请求中,Dispatcher作为第一个接收请求的对象,根据当前正在执行的请求的状况,将新的请求指派到工作队列中并发处理,或者添加到准备队列中缓存起来;不限制单例线程池的最大线程数,减少高并发时额外线程创建的时间耗费,同时不保留最小存活线程数,设置线程空闲60s后销毁,避免资源的长期占用;通过try catch finally 块控制请求队列的执行顺序,而没有使用锁机制,这几个地方的设计都很巧妙。

而Volley的Diapatcher则是由1个缓存线程和默认4个线程的网络线程池组成,线程池采用轮询的机制,这在应对高并发和大数据的请求时并不算高效。


POST请求

内部流程和GET请求基本一样,除了在上述第二步生成的Request对象时,需要额外的RequestBody封装不同类型的请求参数外。

32 Builder-post()



结语

            Okhttp3目前已经有很多通用的第三方封装框架,但是如果配合Rxjava使用,建议使用Okhttp3的同步请求,自己封装一层,可以满足一般项目开发的基本需求。从上面可以看到,其实源码并不是那么难懂,尤其是同步请求,只要多看几遍,即可看到不少Okhttp3的内部运作流程和巧妙之处,这也是建议在配合Rxjava使用时,自己封装一层的原因。当然,如果是配合Retrofit和Rxjava使用,那么就不需要对其过度封装了,因为Retrofit本身就是对Okhttp3的封装库。这次分析流程比较长,还是建议自己写几个例子后,逐步去分析,会对其内部的流程有一个很明了的认知,喜欢的话,给个赞吧。


Okhttp3-网络请求流程解析_第31张图片

        

你可能感兴趣的:(Okhttp3-网络请求流程解析)