OkHttp 源码分析(1)
集成
采用[Gradle][gradle.org]构建方式集成最新版本的[OkHttp][https://github.com…]
dependencies {
implementation "com.squareup.okhttp3:okhttp:4.9.0"
}
使用
初始化
// 初始化客户端
val httpClient:OkHttpClient = OkHttpClient.Builder()
.callTimeout(Duration.ofMillis(TIMEOUT_CALL))
.connectTimeout(Duration.ofMillis(TIMEOUT_CONNECT))
.readTimeout(Duration.ofMillis(TIMEOUT_READ))
.writeTimeout(Duration.ofMillis(TIMEOUT_WRITE))
.build()
// 实例化请求体
val request = Request.Builder()
.url("https://github.com/api")
.header("Content-Type","application/json")
.build()
// 实例化Call
val call =httpClient.newCall(request)
如果使用同步请求
val resposne = call.execute()
resposne?.let { response ->
TODO("Do something")
}
如果使用异步请求
// 如果使用异步请求
call.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")
}
})
相关类
OkHttpClient
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
// 异步任务管理类,内部有一个线程池
val dispatch:Dispatch;
// http连接池,用于连接复用
val connectionPool:ConectionPool;
// 应用拦截器与网络拦截器
val interceptors:List<Interceptor>;
val networkInterceptors:List<Interceptor>;
// 参数
val connectTimeoutMillis:Int;
val readTimeoutMillis:Int;
val writeTimeoutMillis:Int;
// 其他
}
OkHttpClient类中看到的dispatch用于异步任务的管理,实现了线程池的复用,而connectionPool则实现了Http连接池的复用,这两个资源的合理配置实现了OkHttp高并发,低消耗的特点。
同时,OkHttpClient实现了Call工厂方法的接口Call.Factory
fun interface Factory {
fun newCall(request: Request): Call
}
通过newCall可以得到RealCall对象。后文中查看Call接口类,发现该类实现了Cloneable接口,以为其内部会用到对象池技术,结果从OkHttpClient的实现来看,并未使用此技术。
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
Request
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>
) {
}
备注
method字段实现了GET、POST、HEAD、PUT、PATCH、DELETE- 如果
body字段为空,或者本身是可变的,则request对象是可变的
Response
Response是对响应体的抽象
class Response internal constructor(
val request:Request,
val message:String,
val code:Int,
val headers:Headers,
val body:ResponseBody,
...
)
备注
- 该对象是不可变的。因为
body字段是一次性输出。即读取后即关闭状态。
Call
Call是预备执行的请求。
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
}
从上面的方法可以看出以下几个点
Call是可以取消的Call存在未执行与已执行两种状态,他不能或者被执行两次Call存在超时状态,即它的执行时间是受限制的。或者在队列中等待的时间是受限的。Call实现了Clonable接口,猜测是否使用了享元模式,其实不是。Call仅仅只是接口,但具体的实现是RealCall与AsyncRealCall。
Dispatch
class Dispatcher constructor() {
// 最大请求数
var maxRequests = 64
var maxRequestsPerHost=5
// 供外部传入的线程池
var executorServiceOrNull:ExecutorService? = null
// 内部线程池
val executorService:ExecutorService
get(){
if(executorServiceOrNull == null){
executorService = ThreadPoolExecutor(
0,
Int.MAX_VALUE,
60,
TimeUnit.SECONDS,
SynchronousQueue(),
threadFactory("$okHttpName Dispatcher",false)
)
}
}
// 预备状态的异步请求
val readyAsyncCalls = ArrayDeque<AsyncCall>()
// 正在运行的异步请求
val runningAsyncCalls = ArrayDeque<AsyncCall>()
// 正在运行的同步请求
val runningSyncCalls = ArrayDeque<RealCall>()
}
Dispatch主要职能是用于对异步请求的管理。如果外部没有传入用户的线程池,则启用内部自己的线程池,线程池的构造如下
executorService = ThreadPoolExecutor(
0,
Int.MAX_VALUE,
60,
TimeUnit.SECONDS,
SynchronousQueue(),
threadFactory("$okHttpName Dispatcher",false)
)
-
核心线程为0,即常驻线程为0.
-
线程池最大线程数为
Int.MAX_VALUE,但是实际的线程数量会收到maxRequests、maxRequestsPerHost的影响. -
60,TimeUnit.SECONDS当线程处于idle状态60秒后被销毁. -
用于存放任务的容器为
SyschronousQueue,它是线程安全的队列容器,这很重要。 -
最后则是线程工厂.
同步流程分析
同步的调用比较简单,其内部运行并未使用子线程,所以在UI线程中直接调用call.execute()方法会阻塞线程。需要注意的是此时Call接口真正的实现类是RealCall。该类内部execute的实现如下
override fun execute(): Response {
// 通过原子型变量,确保一个Call只被执行一次;
check(executed.compareAndSet(false, true)) { "Already Executed" }
// 打开定时器,在连接超时时候取消请求;
timeout.enter()
// 事件回调,调用栈记录
callStart()
try {
// 将该请求加入队列
client.dispatcher.executed(this)
// 核心方法,通过一系列的拦截器,发起请求,并返回响应;
return getResponseWithInterceptorChain()
} finally {
// 将该请求移出队列
client.dispatcher.finished(this)
}
}
每个RealCall实例对象内部都有一个原子操作型变量
private val executed = AtomicBoolean()
而execute方法中第一件事就是调用executed.compareAndSet(false, true)实现一个具备竟性条件的锁,该锁确保了每个RealCall只被执行一次。并且判断一个RealCall是否被执行过,也是通过该原子型操作读对象
override fun isExecuted(): Boolean = executed.get()
在实行同步锁后,为了能在连接超时后,主动取消连接,RealCall开始进入计时,超时取消连接的功能是由其内部的AsyncTimeout对象实现的
private val timeout = object : AsyncTimeout() {
override fun timedOut() {
// 超时取消连接
cancel()
}
}.apply {
// 初始化连接时间
timeout(client.callTimeoutMillis.toLong(), MILLISECONDS)
}
开启定时器后,Dispatch将此Call对象加入同步队列当中。
/** Used by [Call.execute] to signal it is in-flight. */
@Synchronized internal fun executed(call: RealCall) {
// 同步请求队列
runningSyncCalls.add(call)
}
此时该Call在Dispatch类已经记录为运行状态。
并且在运行最后,无论是否成功或者异常、超时,都将此请求从该队列中移除。
// Dispatch
internal fun finished(call: RealCall) {
finished(runningSyncCalls, call)
}
private fun <T> finished(calls: Deque<T>, call: T) {
val idleCallback: Runnable?
synchronized(this) {
// 如果不在队列中,触发异常
if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
idleCallback = this.idleCallback
}
// 驱动异步事件的检查,确认是否处于空闲状态,无请求
val isRunning = promoteAndExecute()
// 空闲回调
if (!isRunning && idleCallback != null) {
idleCallback.run()
}
}
在上述代码中finished(call:RealCall)方法会调用finished(calls:Deque<T>,call T)方法并且将同步请求队列runningSyncCalls作为参数传入。在finished(calls:Deque,call T) 方法中,通过calls.remove(call)方法将执行的RealCall`移出队列。
需要注意的是,在同步请求结束后,Dispatch会主动去检查异步队列,是否还有未执行的异步请求。该过程是通过promoteAndExecute()方法实现的。如果此时Dispatch中不存在请求,则执行idleCallback.
在同步请求中,RealCall通过getResponseWithInterceptorChain方法获取最终的响应。该方法的内部是以责任链的模式加载一系列的拦截器对象,并最终实现网络访问获取服务端响应的最终目的。拦截器是OkHttp的核心,其内部实现的几个拦截器封装了OkHttp的核心功能。
流程图如下
graph TD
start([start])--> pushQuenen[加入同步请求队列]
pushQuenen --> startTimeout[开启超时计时]
startTimeout -->timeout{是否超时}
timeout--Yes-->cancelCall[取消请求]
cancelCall-->stop
startTimeout --> running[拦截器连接服务器获取响应]
running-->rmQuenen[移除同步队列]
rmQuenen -->scanQuenen[扫描同步异步队列执行队列中请求]
scanQuenen--> stop([end])
拦截器
拦截器接口为
fun interface Interceptor {
@Throws(IOException::class)
fun intercept(chain: Chain): Response
companion object {
// 拦截器的默认实现是调用下个拦截器。
inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =
Interceptor { block(it) }
}
// 责任链模式的链条
interface Chain {
fun request(): Request
@Throws(IOException::class)
fun proceed(request: Request): Response
fun connection(): Connection?
fun call(): Call
....
}
}
Interceptor接口参数为Chain对象,通过Chain对象中的实例方法request()可以获取上个拦截器修饰的Request的对象,同时,通过process(request)方法将当前拦截器修饰的请求传入下个拦截器,并且获取响应。这样就实现了责任链模式。
在同步请求分析中,发现在RealCall中最终是通过getResponseWithInterceptorChain方法获取响应。
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)
// 责任链模式的核心组成,`Interceptor.Chain`接口实现
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)
}
}
}
getResponseWithInterceptorChain的方法核心是初始化一系列的核心拦截器与用户自定义的应用拦截器创建RealInterceptorChain的实例,并且主动调用processed方法。此方法是责任链的起始端。RealInterceptorChain的processed实现如下,他是责任链的开端与重要组成部分
@Throws(IOException::class)
override fun proceed(request: Request): Response {
// indexout
check(index < interceptors.size)
calls++
// 应用拦截器的exchange属性必须为null,
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"
}
}
// 创建新的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
}
通过源码,可以知道OkHttp的核心功能分别封装在如下的几个拦截器中
RetryAndFollowUpInterceptorBridgeInterceptorCacheInterceptorConnectInterceptorCallServerinterceptor
具体如何实现后面再具体研究,此篇只关注流程;
异步流程分析
异步流程大体跟同步流程差不多。主要差别在于Dispatch的异步请求管理。具体步骤依然从外部调用开始。
call.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")
}
})
call.enqueue(callback)发起调用后,Dispatch加入异步队列的是AsyncCall对象.
override fun enqueue(responseCallback: Callback) {
// 同步锁
check(executed.compareAndSet(false, true)) { "Already Executed" }
// 事件下发与堆栈记录
callStart()
// 加入队列的是`AsyncCall`对象,不是`RealCall`对象
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
AsyncCall对象并没有实现Call接口,它是一个Runnable对象。也正是如此,Dispatch中的线程池采用用武之地。
inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {}
Dispatch类中对于异步请求的入队逻辑如下
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
// 加入准备队列,因为受限于最大请求数与单个host最大请求数,
// 所以入队的请求并不能马上执行;
readyAsyncCalls.add(call)
// the same host.
if (!call.call.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
// 超过同一个服务器最大连接数,不具备执行条件
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
}
执行符合条件的异步请求调用了
asyncCall.executeOn(executorService)
其中executorService是线程池对象,在Dispatch中已经分析过了。asyncCall对象是一个Runnable对象。查看executeOn方法如下
fun executeOn(executorService: ExecutorService) {
// 线程锁,一个请求无法被执行两次
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
// 线程池执行runnable对象方法
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
// 如果触发异常,则请求失败
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
// 如果没有触发异常,则请求成功。
if (!success) {
client.dispatcher.finished(this)
}
}
}
该方法的核心是调用executorService.execute(this),this为Runable对象。同时做异常的捕获,如果没有触发异常,则表示异步请求成功,反正则失败。
接下来看Runnable的核心方法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获取响应 不同的地方在于,其结果是通过回调的方式响应给上层。
最后,通过
client.dispatcher.finish(this)将异步请求移出队列。跟同步请求一样,在移出异步队列的同时,通过promoteAndExecute方法再次检查是否存在符合条件的可执行异步请求。