OkHttp在Android开发领域里面应该是无人不知了吧。它是一个由Square公司开源的第三方库。主要用于处理网络请求。
OkHttp Github地址:https://github.com/square/okhttp/
关于Square公司,大家可以去看看他们的其他开源库,质量都挺高的,不仅包括Android相关的,还有Go,Ruby,JS,Kotlin等等
Square官方网站:https://square.github.io/
到目前为止,OkHttp的最新版本是4.3.1,里面一部分代码也已经用Kotlin来重写了,而大部分分析OkHttp源码的文章还停留在以前旧的版本,因此在写这系列源码分析的文章同时,也是学习Kotlin的一个好机会。
如果有做过Android开发的话,相信以下代码大家都很熟悉了:
val client = OkHttpClient()
val request = Request.Builder().url("https://www.baidu.com").build()
val response = client.newCall(request).enqueue(object: Callback{
override fun onFailure(call: Call, e: IOException) {
println("bluelzy --- ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
println("bluelzy --- ${response.body.toString()}")
}
})
这段代码做的事情很简单:
创建一个OkHttpClient对象
使用建造者模式创建一个Request对象
使用OkHttpClient.newCall(request).enqueue() 发起请求,并处理回调
但是这短短的几行代码里面,就已经有很多我们可以学习的东西了。
首先是建造者模式,也叫Builder模式,它的作用是让用户自由组合需要的参数,来实现不同的需求。具体实现方法可以通过接口,也可以像OkHttp一样,使用内部类。这里我们不详细展开阐述,有兴趣的读者可以去看看《大话设计模式》或者《Android源码设计模式分析与实战》。
然后,通过val
这种写法来定义变量也不太优雅,因为这些变量其实声明一次就够了,我们可以改进一下代码:
// 定义Request
Request.Builder().url("https://www.baidu.com").build().let { request ->
// 定义OkHttpClient
OkHttpClient().newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
println("bluelzy --- ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
println("bluelzy --- ${response.body.toString()}")
}
})
}
把声明的三个变量都干掉,这样代码看起来是不是简洁多了?
上面就是一个最简单的OkHttp发起GET请求的方式,下面我们一起来看看OkHttp是怎么做到的。
这个类的作用就是发起HTTP请求和接收响应
首先OkHttpClient有两个构造方法,一个是无参构造方法,另外一个是传入参数为Builder的构造方法
无参构造方法
constructor() : this(Builder())
有参构造方法
open class OkHttpClient internal constructor( builder: Builder )
可以看到,无参构造方法其实也是调用了Builder为参数的构造方法,这里传入的Builder() 就是默认的实现,继续看看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
// Socket工厂
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
// SSL Socket工厂,用于HTTPS
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
// 验证确认响应链,用于HTTPS
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 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
}
Builder是OkHttpClient的内部类,然后Builder声明了很多的参数,这些参数的作用我会在后面的文章中详细分析。
到这里为止,我们就完成了第一步,创建一个OkHttpClient对象,在构造方法里面创建了Builder()对象。然后看第二步:Request.Builder() 创建Request对象
Request的构造方法需要4个参数
class Request internal constructor(
@get:JvmName("url") val url: HttpUrl, // 请求的url
@get:JvmName("method") val method: String, // 请求方法类型
@get:JvmName("headers") val headers: Headers, // 请求头
@get:JvmName("body") val body: RequestBody?, // 请求体
internal val tags: Map<Class<*>, Any>
)
Request同样使用了Builder模式,我们直接看Request.Builder类:
open class Builder {
internal var url: HttpUrl? = null
internal var method: String
internal var headers: Headers.Builder
internal var body: RequestBody? = null
/** A mutable map of tags, or an immutable empty map if we don't have any. */
internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
// 无参构造方法
constructor() {
// 默认为GET
this.method = "GET"
this.headers = Headers.Builder()
}
// 有参构造方法
internal constructor(request: Request) {
this.url = request.url
this.method = request.method
this.body = request.body
this.tags = if (request.tags.isEmpty()) {
mutableMapOf()
} else {
request.tags.toMutableMap()
}
this.headers = request.headers.newBuilder()
}
open fun url(url: HttpUrl): Builder = apply {
this.url = url
}
// 省略代码
}
可以看到,Builder默认的请求方法是GET,并且初始化了一个大小为20的ArrayList作为Headers的容器
除了method和haders之外,还有两个关键的参数,一个是url,另外一个就是RequestBody。这几个参数都提供了set方法,支持链式调用,例如url:
open fun url(url: String): Builder {
// Silently replace web socket URLs with HTTP URLs.
val finalUrl: String = when {
url.startsWith("ws:", ignoreCase = true) -> {
"http:${url.substring(3)}"
}
url.startsWith("wss:", ignoreCase = true) -> {
"https:${url.substring(4)}"
}
else -> url
}
return url(finalUrl.toHttpUrl())
}
这里做了一点处理,如果是基于web socket协议的url,会被替换成http,而wws则是加密的web socket协议。
设置完了url / requestBody / mothod / headers 之后,我们都会调用build()方法:
open fun build(): Request {
return Request(
checkNotNull(url) { "url == null" },
method,
headers.build(),
body,
tags.toImmutableMap()
)
}
其实就是把刚刚设置的作为参数,调用Request的有参构造方法,创建一个Request对象。到这里为止,第二步也就完成了。
做完前面两步之后,终于到了激动人心的时刻了,我们通过OkHttpClient.newCall(request).enqueue()来发起请求
到源码里面看看newCall和enqueue这两个方法分别做了什么
override fun newCall(request: Request): Call {
return RealCall.newRealCall(this, request, forWebSocket = false)
}
调用RealCall.newRealCall方法,并且传入了OkHttpClient和Request作为参数
再看看newRealCall:
companion object {
fun newRealCall(
client: OkHttpClient,
originalRequest: Request,
forWebSocket: Boolean
): RealCall {
// Safely publish the Call instance to the EventListener.
return RealCall(client, originalRequest, forWebSocket).apply {
transmitter = Transmitter(client, this) // okhttp和网络层的中介
}
}
}
其实就是创建了一个RealCall对象,注意这里同时创建了一个Transmitter对象。它后面会再出现的,暂时先忽略,我们先回到发起请求这个过程中来。
上一步创建了RealCall对象是吧,传入了OkHttpClient和Request对象是吧,那么enqueue()方法又做了什么呢?
这个enqueue()方法其实是Call接口的其中一个方法,除了enqueue之外,还有request(), execute()等其他方法。我们先看这个enqueue()方法:
override fun enqueue(responseCallback: Callback) {
synchronized(this) {
check(!executed) { "Already Executed" }
executed = true
}
transmitter.callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
override fun cancel() {
transmitter.cancel()
}
可以看到,首先这个方法有一个Callback作为参数,而这个Callback接口里面又有两个方法:
interface Callback {
/**
* Called when the request could not be executed due to cancellation, a connectivity problem or
* timeout. Because networks can fail during an exchange, it is possible that the remote server
* accepted the request before the failure.
*/
fun onFailure(call: Call, e: IOException)
/**
* Called when the HTTP response was successfully returned by the remote server. The callback may
* proceed to read the response body with [Response.body]. The response is still live until its
* response body is [closed][ResponseBody]. The recipient of the callback may consume the response
* body on another thread.
*
* Note that transport-layer success (receiving a HTTP response code, headers and body) does not
* necessarily indicate application-layer success: `response` may still indicate an unhappy HTTP
* response code like 404 or 500.
*/
@Throws(IOException::class)
fun onResponse(call: Call, response: Response)
}
分别用于处理失败和成功两种情况的回调。
在enqueue的方法体内,首先通过一个synchronized关键字,确保这个方法只会执行一次,
然后通过dispatcher.enqueue来执行异步请求
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
val existingCall = findExistingCallWithHost(call.host())
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
继续看看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 // 最大请求数为64
if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // 相同的Host最大同时请求数为5
i.remove()
asyncCall.callsPerHost().incrementAndGet()
executableCalls.add(asyncCall) // 把请求加入到list中
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService) //
}
return isRunning
}
首先判断是不是超过了最大请求数或者是相同Host的最大请求数,如果是的话就直接return
否则就执行asyncCall.excuteOn方法
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this) // 执行请求
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
transmitter.noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
AsyncCall是RealCall里面的一个内部类,因此在这里已经持有了OkHttpClient对象,也就持有了Dispatcher,而这个executorService又是什么呢?
其实这是一个线程池执行器,在Dispatcher中定义为一个变量
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("OkHttp Dispatcher", false))
}
return executorServiceOrNull!!
}
而 executorService.execute(this) 中的 this
指的就是AsyncCall对象,一个实现了Runnable接口的对象
因此,上面的excuteOn方法,其实就是执行AsyncCall的run()方法啊。
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
transmitter.timeoutEnter()
try {
val response = getResponseWithInterceptorChain() // 1
signalledCallback = true
responseCallback.onResponse(this@RealCall, response) //2
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", 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)
}
}
}
这里又用到了Kotlin的内联函数,相当于在方法体外面多加了一层try…finally,关于内联函数,大家可以看看官方文档的说明:Kotlin中文网 - 内联函数
inline fun threadName(name: String, block: () -> Unit) {
val currentThread = Thread.currentThread()
val oldName = currentThread.name
currentThread.name = name
try {
block()
} finally {
currentThread.name = oldName
}
}
首先我们来看注释1
@Throws(IOException::class)
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(interceptors, transmitter, null, 0, originalRequest, this,
client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
var calledNoMoreExchanges = false
try {
val response = chain.proceed(originalRequest)
if (transmitter.isCanceled) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw transmitter.noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null)
}
}
}
这里用到了另外一个设计模式:责任链模式,用来处理整个网络请求中不同的部分,例如失败重试,缓存,连接等等。这些不同的部分都通过拦截器的方式来实现。
在chain.proceed(originalRequest)
方法中,其实就是调用了RealInterceptorChain.proceed()方法
这个方法的源码:
@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
if (index >= interceptors.size) throw AssertionError()
calls++
// If we already have a stream, confirm that the incoming request will use it.
check(this.exchange == null || this.exchange.connection()!!.supportsUrl(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
check(this.exchange == null || calls <= 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
// Call the next interceptor in the chain.
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
// Confirm that the next interceptor made its required call to chain.proceed().
check(exchange == null || 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
}
可以看到,通过index + 1的方法,我们取出下一个拦截器,然后执行里面的intercept方法,这就是proceed方法主要进行的工作。最终把Response返回。得到的这个Response又通过Callback.onResponse回调方法,使得我们可以获取到这个Response对象。而如果中途抛出异常,那么则会回调onFailure方法。
至于我们在上面加入的那些拦截器,详细的说明会放到下一篇文章中讲解,我们也会加入自定义拦截器的例子。毕竟这种情况在实际开发中还是经常会遇到的,例如自定义ConverterFactory来解析后台返回的数据。
最后,让我们再来回顾一下OkHttp是如何发起请求的:
还有,到目前为止我们已经发现了OkHttp使用了两个设计模式,分别是:
有兴趣的童鞋可以自行上网查找相关资料。
好了,一个简单的请求大致上就是这么个流程,下一篇文章我们继续深入了解OkHttp里面的拦截器~