OkHttp的简单使用
众所周知,讲解一个框架的原理之前都需要知道这个框架如何使用的,都不知道怎么使用的话,直接去分析原理的话,就显得有点头重脚轻了。OkHttp很庞大,当然一篇文章是说不清楚的。哈哈哈哈哈,废话不多说啊,我们先看看简单的使用吧。
val client = OkHttpClient.Builder()
.connectTimeout(1000, TimeUnit.MILLISECONDS)
.callTimeout(2000, TimeUnit.MILLISECONDS)
.build()
val request = Request.Builder()
.url("https://www.baidu.com/")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
TODO("Not yet implemented")
}
override fun onResponse(call: Call, response: Response) {
TODO("Not yet implemented")
}
})
这就是最简单的使用了,实现了一个Get请求,实例化OkHttpClient和Request,再调用newCall传入request对象,再调用enqueue方法实现异步请求,当然我们也可以使用同步调用,那我们就需要开启一个线程去做网络请求。最后就可以在回调方法里拿到成功或是失败的对象。
咱们的分析就从上面提到的OKHttpClient、Request、newCall、enequeue开始说起。
OkHttpClient
OkHttp请求的源头,分析原理的开始。那我们跟随它去看看他处理了什么呢。小儿,上代码。
由于这个类的代码很多,就不都贴出来了。只上一小段啊。
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
其实看这段这就可以了,这是OkHttpClient中的Builder类中的变量。有没有发现其中出现了很熟悉的属性,如connectTimeout、readTimeout等。
具体的源代码大家可以自己去看看,真的一大堆变量。那其实到这里大家应该会明白,在OkHttpClient中其实是做了变量的初始化,初始化OkHttp所必须的一大堆变量,例如分发器、拦截器、缓存、重定向、连接池等。,随便提一嘴,这里用到了建造者模式。建造者模式是什么呢?建造者模式的目的是为了分离对象的属性与创建过程。例如你想构建一个OkHttpClient,如果不用建造者,你就需要去了解这类里面的各个变量都是啥,然后决定要不要赋值,但是使用了建造者之后,你就可以指定几个变量就就可以,内部自己去完成复杂的对象构造过程。
Requset和Response
我们先看看Request里面都有啥吧。
class Request internal constructor(
@get:JvmName("url") val url: HttpUrl,
@get:JvmName("method") val method: String,
@get:JvmName("headers") val headers: Headers,
@get:JvmName("body") val body: RequestBody?,
internal val tags: Map<Class<*>, Any>
) {//....
}
请求的网络所封装的必须信息。请求的url,请求的方法、头信息、请求体。
再来看看Response里面都有啥。
class Response internal constructor(
@get:JvmName("request") val request: Request,
@get:JvmName("protocol") val protocol: Protocol,
@get:JvmName("message") val message: String,
@get:JvmName("code") val code: Int,
@get:JvmName("handshake") val handshake: Handshake?,
@get:JvmName("headers") val headers: Headers,
@get:JvmName("body") val body: ResponseBody?,
@get:JvmName("networkResponse") val networkResponse: Response?,
@get:JvmName("cacheResponse") val cacheResponse: Response?,
@get:JvmName("priorResponse") val priorResponse: Response?,
@get:JvmName("sentRequestAtMillis") val sentRequestAtMillis: Long,
@get:JvmName("receivedResponseAtMillis") val receivedResponseAtMillis: Long,
@get:JvmName("exchange") internal val exchange: Exchange?
) : Closeable {
//.....
}
也是一些相关的变量。
newCall
重头戏来了,我们构建的Request的对象就是通过这个方法发出去的,承载着全村的希望,终于把这个孩子送出去了。那我们去看看内部的实现吧。看它怎么送出去的。
/** Prepares the [request] to be executed at some point in the future. */
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
哦豁,newCall把Request交给了RealCall,它怎么敢的呀。把Request交出去了,返回给我们一个Call对象。然后Call对象去调用同步或异步方法。
RealCall这个类肯定是继承了Call,我们看看Call是个什么吧。
interface Call : Cloneable {
/** Returns the original request that initiated this call. */
fun request(): Request
@Throws(IOException::class)
fun execute(): Response
fun enqueue(responseCallback: Callback)
fun cancel()
fun isExecuted(): Boolean
fun isCanceled(): Boolean
fun timeout(): Timeout
public override fun clone(): Call
fun interface Factory {
fun newCall(request: Request): Call
}
}
原来是一个接口啊,那么RealCall实现了这个接口,所以真实的请求还是交给RealCall来完成的。
enqueue
我们再来看看enqueue方法里面做了啥吧。
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
其他的我们不看,我们只看主要的流程,我们看最后一行代码client.dispatcher.enqueue(),不用我说大家应该猜出来了client是OkHttpClient,最后是调用了client里面的dispatch也就是分发器去做做操作的。
有人就说道理我都懂,那这个AsyncCall是是什么东西呢,好像没见过啊。那我们去看看吧。
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
//.....
}
先看这个AsyncCall继承了Runable接口,那我们可以想到核心代码的执行逻辑肯定在run方法里面,这个大家应该知道吧,好吧,你应该知道了。让我们把run方法里面的代码单独拿出来。
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
大家应该发现了其中里面的一个关键方法getResponseWithInterceptorChain(),看来这是去进行网络请求的关键方法啊。这个方法的返回值就是我们最后在onResponse拿到的值。
getResponseWithInterceptorChain
那我们就去这个方法里面看看具体的实现吧。
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
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)
}
}
}
这就看到了我们的拦截器,OkHttp核心的责任链模式。首先是实例化了一个拦截器的list,然后把我们自定义的拦截器加上了,什么你不知道?我们自定义的拦截器?你看这个client眼不眼熟,那不就是OkHttpClient嘛。然后又把重试和重定向拦截器加上了,再把桥接拦截器加上了,又把缓存拦截器加上了,再把连接拦截器加上了,最后把服务器拦截器加上了。至于每个拦截器做了啥,咱们之后再细细分析哈。这里就简单说几句。
- RetryAndFollowUpInterceptor:重试和重定向
- BridgeInterceptor:构建网络请求需要的信息,如一些头部信息Content-Type、Content-Length等。
- CacheInterceptor:顾名思义,做缓存
- ConnectInterceptor:与服务器做连接的
- CallServerInterceptor:与服务器通信,得到返回的数据
到这里,大家应该明白了,OkHttp的具体调用流程吧。那么有人就问了,不是链式调用嘛。那是在哪里调用的呢。
proceed
不知道大家有没有注意到proceed方法呢。就是在这里开启了链式调用。
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"
}
}
// Call the next interceptor in the chain.
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
}
大家看这段核心代码。
// Call the next interceptor in the chain.
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")
先把index加一表示需要调用下一个拦截器,接着去调用当前拦截器的intercept方法并且把next传入进去。那我们随便看一个拦截器中的实现过程。
override fun intercept(chain: Interceptor.Chain): Response {
//.......
val networkResponse = chain.proceed(requestBuilder.build())
//.......
return responseBuilder.build()
}
上述代码就是BridgeInterceptor的intercept方法,我们看看里面也是调用了proceed方法,如此完成一个链式调用。
OkHttp请求流程
那么到这里主线流程是走完了。至于里面的细节我们下次再谈。敬请期待OkHttp源码解析(二)。