OkHttp简单使用
val client = OkHttpClient.Builder().build()
val request = Request.Builder().url("www.baidu.com").get().build()
//同步请求
val response = client.newCall(request).execute()
//异步请求
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) { }
override fun onFailure(call: Call, e: IOException) { }
})
OkHttpClient是OkHttp对外开放用于网络请求的核心类,通过建造者模式可以进行一些配置,如设置超时时间、添加拦截器等等。Request是请求体对象用于构造url、请求类型、请求参数等,Response是响应体对象,包含完整的http响应体内容。
RealCall请求
OkHttp所有请求都是通过Call对象来处理的,Call只是一个接口,定义了请求的相关方法。RealCall是Call的实现类,每一次真正的请求都被封装成RealCall对象。
interface Call : Cloneable {
fun request(): Request //返回当前请求
fun execute(): Response //同步请求,阻塞线程
fun enqueue(responseCallback: Callback) //异步请求,进入请求队列等待返回
fun cancel() //取消请求
fun isExecuted(): Boolean //请求是否在执行
fun isCanceled(): Boolean //请求是否在执行
fun timeout(): Timeout //返回超时对象
public override fun clone(): Call //克隆新的请求
}
我们具体看看RealCall的同步和异步请求有什么区别
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
从代码来看,不管是同步请求还是异步请求都是通过Dispatcher来调度的。同步请求直接执行,返回Response对象。异步请求构造AsyncCall,传入callback作为回调接口。AsyncCall本质是一个Runnable,Dispatcher通过ExecutorService(线程池)来执行这些Runnable。
Dispatcher调度器
Dispatcher任务调度器,内部维护了三个队列。当发起一个同步或者异步请求时,都会被 Dispatcher保存下来,Dispatcher对请求进行统一的管理,例如结束所有请求、调度线程池资源等等。
val readyAsyncCalls = ArrayDeque<AsyncCall>() //准备运行的异步请求
val runningAsyncCalls = ArrayDeque<AsyncCall>() //正在运行的异步请求
val runningSyncCalls = ArrayDeque<RealCall>() //正在运行的同步请求
拦截器链
拦截器是OkHttp的精髓,包含了从请求到响应整个过程。
不论是同步还是异步最后都会通过getResponseWithInterceptorChina()获取Response,只不过异步请求多了线程调度,异步执行的过程。在getResponseWithInterceptorChina()可以看到所有拦截器。
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
//负责失败重试以及重定向
interceptors += RetryAndFollowUpInterceptor(client)
//负责将request转化为http请求,http返回数据转化为response
interceptors += BridgeInterceptor(client.cookieJar)
//负责读取缓存以及更新缓存
interceptors += CacheInterceptor(client.cache)
//负责与服务器建立连接
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
//负责数据收发
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain()
val response = chain.proceed(originalRequest)
return response
......
}
RealInterceptorChain是带有整个拦截器的具体的拦截器链,proceed方法是执行点。可以理解为在具体拦截器的intercept中,在执行点proceed之前的是用于request请求的,之后则是response响应的过程。这也是OkHttp双向责任链的核心。
override fun proceed(request: Request): Response {
......
calls++
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
val response = interceptor.intercept(next)
return response
}
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor主要负责失败重试以及重定向,由于代理及 DNS 的原因,对于同一个 url 可能会有多个 IP 地址,连接时通过 RouteSelector 选择合适的 Route 进行连接,所以这里的失败重试并不是指对同一 IP 地址的多次重试,是逐个尝试路由表中的地址。
override fun intercept(chain: Interceptor.Chain): Response {
......
var newExchangeFinder = true
while(true) {
call.enterNetworkInterceptorExchange(request, newExchangeFinder)
try {
response = realChain.proceed(request)
} catch(e: Exception) {
//判断是否可以重试
!recover(e.lastConnectException, call, request, false)
newExchangeFinder = false
continue
}
//判断是否需要重定向
val followUp = followUpRequest(response, exchange)
......
return response
}
}
BridgeInterceptor
BridgeInterceptor负责将request转化为http请求,http返回数据转化为response。其中也包括cookie和gzip的处理。
override fun intercept(chain: Interceptor.Chain): Response {
......
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
//添加cookie
requestBuilder.header("Cookie", cookieHeader(cookies))
}
//进入下个拦截器CacheInterceptor
val networkResponse = chain.proceed(requestBuilder.build())
//保存cookie
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&networkResponse.promisesBody()) {
//gzip解析
}
......
}
CacheInterceptor
CacheInterceptor主要用于读取缓存直接返回以及更新缓存。默认是没有开启缓存的,需要在OhHttpClient中设置开启。
override fun intercept(chain: Interceptor.Chain): Response {
//获取缓存策略
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
//缓存策略中设置禁止使用网络,并且缓存又为空,返回504
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.code(HTTP_GATEWAY_TIMEOUT)
.body(EMPTY_RESPONSE)
.build()
}
//缓存策略中设置不使用网络,但是有缓存,直接返回缓存
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
chain.proceed(networkRequest)
if (cache != null) {
//缓存response
val cacheRequest = cache.put(response)
}
return response
}
缓存主要使用了策略工厂的设计模式,通过response,time,request创建一个缓存策略,用于判断怎样使用缓存。其中CacheStrategy类主要用于根据Http中的Cache-control协议,它根据取出来的缓存结果与当前发送的Request的header进行策略计算,得到缓存是否可用的结果。请求响应时,当缓存存在的时候,如果网络返回的Resposne为304,则使用缓存的Resposne。构建网络请求的Resposne当在OkHttpClient中配置了缓存,则将这个Resposne缓存起来。缓存的步骤也是先缓存header,再缓存body,最后返回Resposne。
ConnectInterceptor
ConnectInterceptor主要负责了dns解析和socket连接。
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
//关键代码,dns和socket在此完成
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
CallServerInterceptor
CalllServerInterceptor是最后的拦截器,ConnectInterceptor已经完成了socket连接和tls连接,这一步就是传输http的头部和body数据。这部分的IO操作就是通过Okio来完成的。