携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
前言
OkHttp源码系列文章:
- OkHttp源码之深度解析(一)——整体框架分析
- OkHttp源码之深度解析(二)——拦截器链详解:责任链模式
- OkHttp源码之深度解析(三)——分发器Dispatcher详解
- OkHttp源码之深度解析(四)——RetryAndFollowUpInterceptor详解:重试机制
- OkHttp源码之深度解析(五)——CacheInterceptor详解:缓存机制
OkHttp这个网络请求框架对于很多人来说并不陌生,目前很火的Retrofit也是基于OkHttp的,本文将对OkHttp源码的整体框架和大致流程进行解析。
PS:本文基于OkHttp3版本4.9.3
OkHttp的基本使用
下面是OkHttp的简单使用实例:
//创建OkHttpClient
val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request()
val builder = request.newBuilder()
.addHeader("Token", "...")
chain.proceed(builder.build())
}
.build()
//创建请求
val request = Request.Builder()
.url("http://test.com")
.build()
//发起异步请求
client.newCall(request).enqueue(object : Callback {
......
})
复制代码
代码很简单,先创建出一个OkHttpClient对象和Request对象,然后利用他们去构建一个Call用来发起网络请求,最终拿到Response,下面就来看看框架内部是怎么处理请求过程的。
源码分析
1. OkHttpClient类
OkHttpClient类的主要职责就是让用户配置一些请求参数(如interceptor、connectTimeout等等),采用建造者模式通过Builder类去配置OkHttpClient的成员变量,最后调用build方法创建出OkHttpClient实例。
在OkHttpClient类中的主要成员变量有:
- dispatcher: Dispatcher,用于调度网络请求的分发器
- interceptors:
MutableList<Interceptor>
,拦截器集合 - networkInterceptors:
MutableList<Interceptor>
,用户自定义的网络拦截器 - connectionPool: ConnectionPool,连接池
- protocols:
List<Protocol>
,支持的Http协议版本,即HTTP/1.1、HTTP/2等 - cache: Cache,缓存配置,默认是没有
- cookieJar: CookieJar,cookie配置
- callTimeout: Int,请求超时,默认是0
- connectTimeout: Int,连接超时,默认10秒
- readTimeout: Int,读取超时,默认10秒
- writeTimeout: Int,写入超时,默认10秒
- pingInterval: Int,发送ping指令间隔,与WebSocket有关,为了保持长连接
- retryOnConnectionFailure: Boolean,连接失败是否重连
- followRedirects: Boolean,是否重定向
- ollowSslRedirects: Boolean,是否从HTTP重定向到HTTPS
- hostnameVerifier: HostnameVerifier,域名校验
- eventListenerFactory: EventListener.Factory,Call的生命周期监听器
OkHttpClient中的参数都不是必要的配置项,如果初始化OkHttpClient的时候没有设置则会使用默认配置,而且这些配置会被所有call请求共享。
2. Request类
Request类的职责与OkHttpClient类相似,也是用来配置请求参数的,通过Builder类去配置以下的参数:
- url: HttpUrl,请求url
- method: String,请求方法,如果没有指定的话会默认配置GET
- headers: Headers,请求头
- body: RequestBody,请求体
- tags: Map<Class<*>, Any>,请求标签
3. RealCall类
在OkHttpClient对象调用newCall方法创建Call的时候实际上就会进入到RealCall类中:
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
复制代码
也就是构建Call的工作是由RealCall去做的,RealCall是连接应用和网络层的桥梁,通过调用RealCall的同步请求execute方法或异步请求enqueue方法发起请求最后拿到Response,下面来分析RealCall类中的主要方法。
3.1 同步请求execute方法
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)
}
}
复制代码
execute方法主要做了以下的工作:
- 检测这个Call是否已经被执行过,一个Call只能被执行一次,如果已经执行过则抛出异常
- 开启请求超时的计时和开启请求生命周期的监听
- 调用Dispatcher的executed方法将这个RealCall对象加入到正在执行的Call队列中
- 调用getResponseWithInterceptorChain方法构建拦截器链,遍历拦截器链后返回Response
- 请求执行完后调用Dispatcher的finish方法将这个Call从队列中移除
3.2 getResponseWithInterceptorChain方法
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
......//省略一堆构建拦截器列表的逻辑
//构建拦截器链
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) {
......//异常处理
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
复制代码
getResponseWithInterceptorChain方法是整个OkHttp框架执行请求的核心所在,采用了责任链模式,本文先对拦截器责任链的构建有个整体了解,后续的文章会详细分析。getResponseWithInterceptorChain方法主要做了以下的事:
- 根据用户自定义的拦截器和OkHttp内置的拦截器按顺序构建出拦截器列表
- 构建拦截器责任链RealInterceptorChain
- 调用RealInterceptorChain的proceed方法遍历拦截器链执行请求,直到所有拦截器都完成拦截最后返回Response
- 当请求被取消时关闭响应并抛出异常
3.3 异步请求enqueue方法
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
复制代码
enqueue方法同样先是检测Call是否已被执行,最后调用Dispatcher的enqueue方法去执行请求,值得注意的是同步请求生成的是RealCall对象,而异步请求生成的是AsyncCall对象。
另外这个callStart方法同步和异步都有调用到,方法内部调用到了EventListener的callStart:
private fun callStart() {
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
eventListener.callStart(this)
}
复制代码
追踪源码发现EventListener的所有回调都是在一个叫LoggingEventListener里面实现的,LoggingEventListener只是负责在Call的每个状态打日志,由此推断出EventListener是用于监听Call的生命周期的,callStart这个回调会在请求开始的时候被触发,当然如果在开发中有特殊要求的话可以自定义EventListenerFactory用于生产需要的EventListener,并在配置OkHttpClient的时候传进去。
4. AsyncCall类
AsyncCall类是定义在RealCall类中的一个内部类,AsyncCall实现了Runnable接口,里面主要是executeOn方法和重写了run方法,先来看看executeOn方法的源码:
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this) //调用线程池执行请求
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
......//异常处理
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this)
}
}
}
复制代码
executeOn方法主要是通过入参传入一个线程池,并调用这个线程池执行请求,请求失败则抛出异常并将异常通过回调返回去,最后调用Dispatcher的finish方法将这个请求移出队列。再来看下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) {
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)
}
}
}
复制代码
由于AsyncCall实现了Runnable接口,所以run方法是异步的具体实现:
- 跟同步请求一样通过调用getResponseWithInterceptorChain方法来获取请求结果,由于AsyncCall是RealCall的内部类,因此可以直接访问RealCall的方法getResponseWithInterceptorChain而不需要通过一个RealCall实例去调用
- 将拿到的Response通过CallBack.onResponse返回
- 如果请求失败则将错误信息通过CallBack.onFailure返回
- 如果请求异常则取消请求,同时将异常信息抛出并回调
- 最后在请求结束时调用Dispatcher的finish方法将这个请求移出队列
除了上面提到的两个主要方法之外,AsyncCall内部还持有了一个RealCall实例:
val call: RealCall
get() = this@RealCall
复制代码
然后在追踪这个RealCall实例的调用处发现,这个对象只是给Dispatcher用来访问RealCall里的成员的,而AsyncCall本身并没有用到它。
对于这一块本人有个疑惑,RealCall实现了Call接口,AsyncCall实现了Runnable接口,而AsyncCall是定义在RealCall中的inner class,两者之间不存在继承的关系,在AsyncCall中又持有着一个RealCall对象,有种套娃的感觉。在处理异步请求时Dispatcher需要访问RealCall类里面的成员,这时只能通过AsyncCall持有的RealCall实例去访问,既然这样那为什么AsyncCall不直接继承于RealCall呢?继承于RealCall也并不影响AsyncCall实现Runnable接口,这样AsyncCall直接就是一个RealCall了,Dispatcher不就直接能访问里面的成员了吗?至于为什么要这样设计我也不太想明白,欢迎各位朋友在评论区谈谈自己的见解。
5. Dispatcher类
在看RealCall的源码时不难发现,无论是同步请求还是异步请求,都会进入到分发器Dispatcher中做相应的处理,下面就来分析一下Dispatcher的主要职责是什么,先来看下Dispatcher中的成员变量:
class Dispatcher constructor() {
@get:Synchronized var maxRequests = 64
@get:Synchronized var maxRequestsPerHost = 5
@set:Synchronized
@get:Synchronized
var idleCallback: Runnable? = null
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
private val runningSyncCalls = ArrayDeque<RealCall>()
......
}
复制代码
Dispatcher中定义了以下的成员变量:
- maxRequests:并行的最大的请求数为64个
- maxRequestsPerHost:同一个host并行的最大请求数为5个
- idleCallback:分发器空闲时的回调
- executorService:线程池
- readyAsyncCalls:已准备好的异步请求队列
- runningAsyncCalls:正在执行的异步请求队列,包含已经被取消但还没完成的AsyncCall
- runningSyncCalls:正在执行的同步请求队列,包含已经被取消但还没完成的RealCall
除了这些成员变量还定义了一些用来调度Call的方法,也就是说Dispatcher的主要职责就是用来调度Call对象的,内部维护了一个线程池和一些同步/异步请求队列,用于存放和调度Call。
请求流程
到这里OkHttp的整体框架大致分析完了,请求的时候OkHttpClient会通过newCall方法去访问RealCall,如果是发起同步请求则会生成一个RealCall对象通过execute方法传给分发器Dispatcher处理,如果是异步请求则会生成一个AsyncCall对象传给Dispatcher,无论是同步请求还是异步请求最终都是通过getResponseWithInterceptorChain方法去遍历拦截器链获取Response,下面是OkHttp的简单请求流程:
sequenceDiagram
OkHttpClient->>RealCall: newCall(Request)
RealCall->>RealInterceptorChain:getResponseWithInterceptorChain
RealCall->>Dispatcher: execute(RealCall)
RealCall-->>AsyncCall:enqueue
AsyncCall-->>Dispatcher:enqueue(AsyncCall)
Dispatcher-->>AsyncCall:promoteAndExecute
AsyncCall-->>RealInterceptorChain:getResponseWithInterceptorChain
RealInterceptorChain->>RealCall:Response
RealCall->>OkHttpClient:Response
PS:本文仅对OkHttp从大体框架上进行说明,Dispatcher、拦截器等具体模块的详细分析敬请关注后续文章,另外此文章仅代表个人见解,理解有偏差的地方欢迎各位大佬指出。