OkHttp3的基本用法
使用OkHttp3发送Http请求并获得响应的过程大体为:
- 创建OkHttpClient对象。OkHttpClient为网络请求执行的一个中心,它会管理连接池,缓存,SocketFactory,代理,各种超时时间,DNS,请求执行结果的分发等许多内容。
- 创建Request对象。Request用于描述一个HTTP请求,比如请求的方法是”GET”还是”POST”,请求的URL,请求的header,请求的body,请求的缓存策略等。
- 利用前面创建的OkHttpClient对象和Request对象创建Call对象。Call是一次HTTP请求的Task,它会执行网络请求以获得响应。OkHttp中的网络请求执行Call既可以同步进行,也可以异步进行。调用call.execute()将直接执行网络请求,阻塞直到获得响应。而调用call.enqueue()传入回调,则会将Call放入一个异步执行队列,由ExecutorService在后台执行。
- 执行网络请求并获取响应。
通过一段示例代码来看一下具体要如何操作:
Call的执行
(后面所有的代码分析都基于正式的发布版本3.4.0进行,’com.squareup.okhttp3:okhttp:3.4.0’,由于OkHttp当前依然处于比较活跃的开发状态,因而不同版本的内部实现相对于我们当前分析的这一版有可能会有比较大的变化。)
Call是一个接口,其定义如下:
Call中还定义了一个Factory接口。
那在OkHttp中,我们调用的Call方法的实际执行过程是怎样的呢?这就需要扒出来在OkHttp中实际使用的Call实现了。OkHttpClient实现了Call.Factory接口,通过接口方法OkHttpClient.newCall()可以看到具体使用的Call实现是哪个类:
在OkHttp中使用了RealCall来执行整个Http请求。
通过调用RealCall.execute()同步执行Http请求的过程大体为:
- 调用client.dispatcher().executed(this)向client的dispatcher注册当前Call。
- 调用getResponseWithInterceptorChain()执行网络请求并获得响应。
- 调用client.dispatcher().finished(this)向client的dispatcher注销当前Call。
通过调用RealCall.enqueue()异步执行Http请求的过程则为,创建AsyncCall并将之丢给client的dispatcher。而在RealCall.AsyncCall的execute()中执行Http请求的过程与RealCall.execute()中的过程有些类似:
- 调用getResponseWithInterceptorChain()执行网络请求并获得响应。
- 调用Callback回调通知用户执行的结果。可以看到这里对回调接口是同步调用,也就是回调方法将在后台线程中被调用。
- 调用client.dispatcher().finished(this)向client的dispatcher注销当前Call。
这里再通过Dispatcher的定义来看一下在OkHttp中,请求的执行管理及异步执行是怎么做的:
在Call的同步执行过程中,调用client.dispatcher().executed(this)向client的dispatcher注册当前Call,Dispatcher仅仅是将Call放进了runningSyncCalls,其它便什么也没做,目测同步执行Call时向Dispatcher注册的主要目的是方便全局性的cancel所有的Call。
Dispatcher中异步的AsyncCall是被放在一个ExecutorService中执行的。默认情况下,这是一个不限容量的线程池。但Dispatcher会限制每个host同时执行的最大请求数量,默认为5,同时也会限制同时执行的总的最大请求数量。runningAsyncCalls中保存所有正在被ExecutorService执行的AsyncCall,而readyAsyncCalls则用于存放由于对单个host同时执行的最大请求数量的限制,或总的同时执行最大请求数量的限制,而暂时得不到执行的AsyncCall。
finished()中,除了会将执行结束的AsyncCall从runningAsyncCalls移除之外,还会检查是否存在由于 单host同时进行的最大请求数量限制 或 总的同时执行最大请求数量限制,而暂时得不到执行的AsyncCall,若存在则满足限制条件的请求会被执行。
所有的Call,不管是异步的AsyncCall还是同步的Call在执行结束后都会检查是否没有正在进行的Http请求了。若没有了,则存在idle 回调时,该回调会被调用。
用户可以通过Dispatcher的构造函数来定制ExecutorService,这需要通过OkHttpClient.Builder在OkHttpClient的构建过程中间接的做到。
回到RealCall,继续来追Call的网络请求及响应处理。来看一下RealCall.getResponseWithInterceptorChain():
这里主要是创建了一个Interceptor的列表,继而创建了一个Interceptor.Chain对象来处理请求并获得响应。我们继续追踪一下RealInterceptorChain:
在RealInterceptorChain.proceed()中,除了对状态及获取的reponse做检查之外,最主要的事情即是构造新的RealInterceptorChain对象,获取对应Interceptor,并调用Interceptor的intercept(next)了。在这里,index充当迭代器或指示器的角色,用于指出当前正在处理的Interceptor。
RealInterceptorChain + Interceptor实现了装饰器模式,实现了请求/响应的串式或流式处理。只不过内层装饰器不是外层装饰器的成员变量,而是接口方法中创建的临时变量。
但Interceptor链中具体都有哪些Interceptor呢?我们就在RealCall.getResponseWithInterceptorChain()中打个断点来看一下:
由此可见OkHttp中,Http请求的实际处理流程将大致如下图这样:
RetryAndFollowUpInterceptor
具体这些Interceptor中每一个都会做些什么事情呢?我们后面再来详细地做分析。
首先来看RetryAndFollowUpInterceptor:
RetryAndFollowUpInterceptor
在intercept()
中首先从client取得connection pool,用所请求的URL创建Address对象,并以此创建StreamAllocation对象。
Address描述某一个特定的服务器地址。StreamAllocation对象则用于分配一个到特定的服务器地址的流HttpStream,这个HttpStream可能是从connection pool中取得的之前没有释放的连接,也可能是重新分配的。RetryAndFollowUpInterceptor这里算是为后面的操作准备执行条件StreamAllocation。
随后RetryAndFollowUpInterceptor.intercept()
利用Interceptor链中后面的Interceptor来获取网络响应。并检查是否为重定向响应。若不是就将响应返回,若是则做进一步处理。
对于重定向的响应,RetryAndFollowUpInterceptor.intercept()
会利用响应的信息创建一个新的请求。并检查新请求的服务器地址与老地址是否相同,若不相同则会根据新的地址创建Address对象及StreamAllocation对象。
RetryAndFollowUpInterceptor
对重定向的响应也不会无休止的处理下去,它处理的最多的重定向级数为20次,超过20次时,它会抛异常出来。
RetryAndFollowUpInterceptor
通过followUpRequest()从响应的信息中提取出重定向的信息,并构造新的网络请求:
我们知道OkHttp提供了非常好用的容错功能,它可以从某些类型的网络错误中恢复,即出错重试机制。这种出错重试机制主要由recover()来实现:
主要是对某些类型IOException的恢复,恢复的次数会由StreamAllocation控制。
总结一下RetryAndFollowUpInterceptor
做的事情:
- 创建StreamAllocation对象,为后面流程的执行准备条件。
- 处理重定向的HTTP响应。
- 错误恢复。
BridgeInterceptor
如我们在RealCall.getResponseWithInterceptorChain()
中所见,紧接在RetryAndFollowUpInterceptor
之后的Interceptor
是BridgeInterceptor
:
这个Interceptor做的事情比较简单。可以分为发送请求和收到响应两个阶段来看。在发送请求阶段,BridgeInterceptor补全一些http header,这主要包括Content-Type
、Content-Length
、Transfer-Encoding
、Host
、Connection
、Accept-Encoding
、User-Agent
,还加载Cookie
,随后创建新的Request,并交给后续的Interceptor处理,以获取响应。
而在从后续的Interceptor获取响应之后,会首先保存Cookie
。如果服务器返回的响应的content是以gzip压缩过的,则会先进行解压缩,移除响应中的header Content-Encoding
和Content-Length
,构造新的响应并返回;否则直接返回响应。
CookieJar
来自于OkHttpClient
,它是OkHttp的Cookie
管理器,负责Cookie
的存取:
由OkHttpClient
默认的构造过程可以看到,OkHttp中默认是没有提供Cookie管理功能的。由这里的代码,我们大概也能知道要支持Cookie的话,需要做些什么事情。
CacheInterceptor
CacheInterceptor紧接于BridgeInterceptor之后,它主要用来处理缓存:
对于CacheInterceptor.intercept(Chain chain)
的分析同样可以分为两个阶段,即请求发送阶段和响应获取之后的阶段。这两个阶段由chain.proceed(networkRequest)
来分割。
在请求发送阶段,主要是尝试从cache中获取响应,获取成功的话,且响应可用未过期,则响应会被直接返回;否则通过后续的Interceptor来从网络获取,获取到响应之后,若需要缓存的,则缓存起来。
关于HTTP具体的缓存策略这里暂时不再详述。
由RealCall.getResponseWithInterceptorChain()
可见CacheInterceptor的cache同样来自于OkHttpClient。OkHttp已经有实现Cache的整套策略,在Cache类,但默认情况下不会被用起来,需要自己在创建OkHttpClient时,手动创建并传给OkHttpClient.Builder。
ConnectInterceptor
CacheInterceptor接下来是ConnectInterceptor:
这个类的定义看上去倒是蛮简洁的。ConnectInterceptor的主要职责是建立与服务器之间的连接,但这个事情它主要是委托给StreamAllocation来完成的。如我们前面看到的,StreamAllocation对象是在RetryAndFollowUpInterceptor中分配的。
ConnectInterceptor通过StreamAllocation创建了HttpStream对象和RealConnection对象,随后便调用了realChain.proceed(),向连接中写入HTTP请求,并从服务器读回响应。
连接建立过程的更多细节我们这里先不详述。
CallServerInterceptor
ConnectInterceptor之后是CallServerInterceptor,这也是这个链中的最后一个Interceptor,它的主要职责是处理IO:
CallServerInterceptor
首先将http请求头部发给服务器,如果http请求有body的话,会再将body发送给服务器,继而通过httpStream.finishRequest()
结束http请求的发送。
随后便是从连接中读取服务器返回的http响应,并构造Response。
如果请求的header或服务器响应的header中,Connection
值为close
,CallServerInterceptor
还会关闭连接。
最后便是返回Response。
总结一下这几个Interceptor的职责:
RetryAndFollowUpInterceptor —>创建StreamAllocation对象,处理http的redirect,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。
BridgeInterceptor————–>补全缺失的一些http header。对后续Interceptor的执行的影响:修改request。
CacheInterceptor————–>处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor不再执行。
ConnectInterceptor————>借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响:创建了httpStream和connection。
CallServerInterceptor———–>处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor。
End。