1 OkHttp 简介
目前主流的Android 网络请求框架有OkHttp 和Retrofit ,不过,Retrofit 底层使用的是OKHttp ,其自身是不具备网络请求能力的。
OkHttp 是由Square公司开发并共享开源的高效网络访问框架,使用简单,它替代了HttpUrlConnection 和Apache 的HttpClient 。
默认情况下OkHttp 具备以下特性:
- 支持
HTTP/2 ,允许同一个主机地址的所有请求共享同一个Socket 连接 - 连接池减少请求延时
- 透明的
GZIP 压缩较少响应数据的大小 - 缓存响应内容,避免一些完全重复的请求
- 网络出现问题后,
OkHttp 保持不变,自动从问题中恢复
下面是Http 常见的一些状态码:
100~199 :指示信息,表示请求已接收,继续处理;200~299 :请求成功,表示请求已被成功接收、理解;300~399 :重定向,要完成请求必须进行更进一步的操作;400~499 :客户端错误,请求有语法错误或请求无法实现;500~599 :服务器端错误,服务器未能实现合法的请求;
2 OkHttp 请求流程
以下是OkHttp 发起请求的大致流程:
在使用OkHttp 进行请求时,首先要创建一个OkHttpClient 的实例:
val client = OkHttpClient()
如果想要发起一条HTTP 请求,就需要创建一个Request 对象:
val request = Request.Builder().url(url).build()
之后调用OkHttpClient 的newCall 方法来创建一个Call 对象,并调用它的execute/enqueue 方法来发送请求并获取服务器返回的数据,其中,execute() 方法是同步方法,enqueue() 方法是异步方法:
val response = client.newCall(request).execute()
response 对象就是服务器返回的数据,可以使用如下写法来的到返回的具体内容:
val responseData = response.body?.string()
下面是OkHttp 进行GET 请求/POST 请求的代码:
private val client = OkHttpClient()
fun getMethod(url: String): String {
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
return response.body?.string() ?: ""
}
val JSON = "application/json; charset=utf-8".toMediaType()
fun postMethod(url: String, json: String): String {
val body = RequestBody.create(JSON, json)
val request = Request.Builder().url(url).post(body).build()
val response = client.newCall(request).execute()
return response.body?.string() ?: ""
}
2.1 OkHttpClient
在使用OkHttpClient 之前,需要先创建一个OkHttpClient 客户端,OkHttpClient 的构造方法如下:
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
constructor() : this(Builder())
}
可以看到,OkHttpClient 使用了建造者模式,Builder 里面的可配置参数如下:
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
constructor() : this(Builder())
class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher()
internal var connectionPool: ConnectionPool = ConnectionPool()
internal val interceptors: MutableList<Interceptor> = mutableListOf()
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
internal var retryOnConnectionFailure = true
internal var authenticator: Authenticator = Authenticator.NONE
internal var followRedirects = true
internal var followSslRedirects = true
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
internal var cache: Cache? = null
internal var dns: Dns = Dns.SYSTEM
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
internal var x509TrustManagerOrNull: X509TrustManager? = null
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
internal var certificateChainCleaner: CertificateChainCleaner? = null
internal var callTimeout = 0
internal var connectTimeout = 10_000
internal var readTimeout = 10_000
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
internal var routeDatabase: RouteDatabase? = null
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.connectionPool = okHttpClient.connectionPool
this.interceptors += okHttpClient.interceptors
this.networkInterceptors += okHttpClient.networkInterceptors
this.eventListenerFactory = okHttpClient.eventListenerFactory
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
this.authenticator = okHttpClient.authenticator
this.followRedirects = okHttpClient.followRedirects
this.followSslRedirects = okHttpClient.followSslRedirects
this.cookieJar = okHttpClient.cookieJar
this.cache = okHttpClient.cache
this.dns = okHttpClient.dns
this.proxy = okHttpClient.proxy
this.proxySelector = okHttpClient.proxySelector
this.proxyAuthenticator = okHttpClient.proxyAuthenticator
this.socketFactory = okHttpClient.socketFactory
this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
this.x509TrustManagerOrNull = okHttpClient.x509TrustManager
this.connectionSpecs = okHttpClient.connectionSpecs
this.protocols = okHttpClient.protocols
this.hostnameVerifier = okHttpClient.hostnameVerifier
this.certificatePinner = okHttpClient.certificatePinner
this.certificateChainCleaner = okHttpClient.certificateChainCleaner
this.callTimeout = okHttpClient.callTimeoutMillis
this.connectTimeout = okHttpClient.connectTimeoutMillis
this.readTimeout = okHttpClient.readTimeoutMillis
this.writeTimeout = okHttpClient.writeTimeoutMillis
this.pingInterval = okHttpClient.pingIntervalMillis
this.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompress
this.routeDatabase = okHttpClient.routeDatabase
}
}
}
2.2 同步请求
同步请求是指发出网络请求之后当前线程被阻塞,直到请求的结果(成功或者失败)到来,才继续向下执行。同步请求使用的是execute 方法,如下所示:
val response = client.newCall(request).execute()
以下是源码:
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
}
class RealCall(
val client: OkHttpClient,
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
internal val eventListener: EventListener = client.eventListenerFactory.create(this)
val call: RealCall
get() = this@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)
}
}
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
}
class RealInterceptorChain(
internal val call: RealCall,
private val interceptors: List<Interceptor>,
private val index: Int,
internal val exchange: Exchange?,
internal val request: Request,
internal val connectTimeoutMillis: Int,
internal val readTimeoutMillis: Int,
internal val writeTimeoutMillis: Int
) : Interceptor.Chain {
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
}
同步请求交给了RealCall 类,是RealCall 类执行了execute 方法。
2.3 异步请求
异步请求是指网络请求发出之后,不必等待请求结果的到来,就可以去做其他的事情,当请求结果到来时,在做处理结果的动作。异步请求使用的是enqueue 方法,如下所示:
private val client = OkHttpClient()
fun run() {
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
for ((name, value) in response.headers) {
println("$name: $value")
}
println(response.body!!.string())
}
}
})
}
fun run() {
val formBody = FormBody.Builder()
.add("search", "Jurassic Park")
.build()
val request = Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(formBody)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
println(response.body!!.string())
}
}
以下是源码:
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
}
class RealCall(
val client: OkHttpClient,
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
}
class Dispatcher constructor() {
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
private val runningSyncCalls = ArrayDeque<RealCall>()
private var executorServiceOrNull: ExecutorService? = null
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
constructor(executorService: ExecutorService) : this() {
this.executorServiceOrNull = executorService
}
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
}
异步请求是调度器dispatcher 执行—client.dispatcher.enqueue(AsyncCall(responseCallback)) ,并通过回调(Callback )获取服务器返回的结果。
Dispatcher 将call 加入到队列中,然后通过线程池来执行call 。Dispatcher 是一个任务调度器,它内部维护了三个双端队列:
readyAsyncCalls :准备运行的异步请求;runningAsyncCalls :正在运行的异步请求;runningSyncCalls :正在运行的同步请求;
新来请求放在队尾,执行请求从对头部取。
OKHttp 双任务队列机制:
OkHttp 实现异步请求采用了双任务队列机制,通过Dispatcher 来调度任务- 新加入的异步任务
AsyncCall 进入等待队列readyAsyncCalls - 遍历
readyAsyncCalls 判断当前情况:是否超过最大并发数?是否超过同个主机最大请求数? - 满足条件直接把
AsyncCall 加入到正在执行的队列RunningAsyncCalls ,并且使用线程池执行新加入的异步任务AsyncCall AsyncCall 执行结束,再次回到Dispatcher 的promoteAndExecute()
3 责任链模式与拦截器
责任链模式(Chain of Responsibility Pattern )为请求创建了一个接收者对象的链,请求在这条链上传递,直到链上的某个对象处理此请求,或者每个对象都可以处理请求,并传给“下家”,直到最终链上每个对象都处理完。
这种模式可以避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求。
OkHttp 拦截器就是基于责任链模式来实现的, 在请求到达时,拦截器会做一些处理(比如添加参数),然后传递给下一个拦截器处理:
4 OkHttp 的拦截器
OkHttp 提供了一系列的拦截器来处理相应的业务。也可以通过自定义拦截器,来实现自己的拦截业务。下面是一些常用的拦截器:
retryAndFollowUpInterceptor :失败和重定向拦截器。当请求内部抛出异常时,判定是否需要重试,当响应结果是3xx 重定向时,构建新的请求并发送请求;BridgeInterceptor :封装request 和response 拦截,负责把用户构造的请求转换为发送到服务器的请求,把服务器返回的响应转换为用户友好的响应;CacheInterceptor :如果在OkHttpClient 中配置了缓存,则将这个Resposne 缓存起来;ConnectInterceptor :连接服务,负责和服务器建立连接,这负责Dns 解析和Socket 连接;CallServerInterceptor :传输http 的头部和body 数据;
OKHttp 中完整的拦截链如下所示,在真正发起请求之前(CallServerInterceptor ),将经过各个拦截器处理业务:
4.1 CacheInterceptor 网络请求缓存处理
OkHttp 的缓存原则是,缓存拦截器会根据请求的信息和缓存的响应的信息来判断是否存在缓存可用,如果有可以使用的缓存,那么就返回该缓存给用户,否则就继续使用责任链模式来从服务器中获取响应。 当获取到响应的时候,又会把响应缓存到磁盘上面。涉及到的代码有:
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
if (cacheCandidate != null && cacheResponse == null) {
cacheCandidate.body?.closeQuietly()
}
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = null
try {
networkResponse = chain.proceed(networkRequest)
} finally {
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
networkResponse.body!!.close()
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
listener.cacheMiss(call)
}
}
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
}
}
}
return response
}
}
4.2 addInterceptor 和addNetworkdInterceptor 区别
下面是构造拦截器链的过程,可以看到,通过addInterceptor() 方法添加的拦截器是放在最前面的,而通过addNetworkInterceptor() 方法添加的网络拦截器,则是在非WebSocket 请求时,添加在ConnectInterceptor 和CallServerInterceptor 之间的:
class RealCall(
val client: OkHttpClient,
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
internal val eventListener: EventListener = client.eventListenerFactory.create(this)
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
}
addInterceptor (应用拦截器):
- 不需要担心中间过程的响应,如重定向和重试;
- 总是只调用一次,即使
HTTP 响应是从缓存中获取; - 观察应用程序的初衷,不关心
OkHttp 注入的头信息,如:If-None-Match ; - 允许短路而不调用
Chain.proceed() ,即中止调用; - 允许重试,使
Chain.proceed() 调用多次;
addNetworkInterceptor (网络拦截器):
- 能够操作中间过程的响应,如重定向和重试;
- 当网络短路而返回缓存响应时不被调用;
- 只观察在网络上传输的数据;
- 携带请求来访问连接;
5 连接池
5.1 TCP 三次握手和四次挥手
- 三次握手:客户端发送请求建立连接,服务端收到请求后立即回应,客户端收到回应后打开连接并通知服务端,服务端再次收到消息也打开连接;
- 四次挥手:客户端数据发送完成告知服务端申请断开,服务端收到断并回应,客户端继续等待最后数据的传送,服务端业务完成再次发送回应消息并断开连接,客户端收到回应,再次发送一次确认,并断开;
5.2 Socket 连接池复用
连接池是为了解决频繁的进行建立Sokcet 连接(TCP 三次握手)和断开Socket (TCP 四次分手)。每次建立连接关闭连接都要三次握手、四次挥手,显然会造成效率低下,Http 协议中有一种叫做KeepAlive 机制,它可以在传输数据后仍然保持连接状态,当客户端需要再次传输数据时,直接使用空闲下来的连接而不需要重新建立连接。
OkHttp 默认支持5 个并发KeepAlive ,链路默认的存活时间为5 分钟:
参考
https://blog.csdn.net/OneDeveloper/article/details/88381817
|