OkHttp请求流程
下面的代码简单演示了发送一个post请求的过程:
//发送一个post请求
private fun sendPostRequest(){
//请求地址
val address = "请求地址"
//创建请求体
val requestBody = FormBody.Builder().let {
it.add("username","test")
it.add("password","123456")
it.build()
}
//创建请求信息
val request = Request.Builder().let {
it.url(address)
it.post(requestBody)
it.build()
}
//创建客户端
val client = OkHttpClient()
client.newCall(request = request).enqueue(object : Callback{
override fun onFailure(call: Call, e: IOException) {
Logs.e("请求出错:$e")
}
override fun onResponse(call: Call, response: Response) {
Logs.e("请求完成:${response.body!!.string()}")
}
})
}
通过上面的代码,我们就可以向服务器发送一个post请求。
基本流程
这篇学习笔记的侧重点是对请求的整个流程进行一个梳理,大体了解一个网络请求都经过哪些过程。网络请求的过程描述起来相对简单,就跟取快递是一样的,拿着你取快递的短信(请求体),到快递点(请求地址),取回你的快递(响应体)。
但是实现起来是很困难的,正常情况下,我们会首先创建一个请求Request,向这个请求中写入我们需要传递的数据RequestBody,通过OkHttpClient将这个请求发送给服务器,并接收到服务器返回的数据。下面的学习不会对可能出现的异常进行解释,默认请求都是正常执行完成的,主要来看OkHttpClient在这个过程中做了哪些操作。
创建OkHttpClient
使用下面的代码创建一个OkHttpClient:
val client = OkHttpClient()
这里调用了OkHttpClient无参的构造方法,下面是这样方法的源码:
constructor() : this(Builder())
可以看到,这里使用了建造者模式,通过Builder()来构建一个具有默认参数的OkHttpClient,Builder()方法就是对使用到的参数设置值,这里使用的是默认值。
在这里需要注意的是,我们通过OkHttpClient.Builder()构造函数创建了一个具有默认参数的Builder,然后将这个Builder传递给OkHttpClient(Builder)来返回创建的OkHttpClient对象,调用了OkHttpClient(Builder)方法还会调用init{}代码块设置具体的参数。
OkHttpClient.newCall()
创建完OkHttpClient对象之后,接下来会调用newCall()方法,会将上面创建的Request传递到这个方法内部,下面是这个方法的源码:
//client.newCall(request = request)调用的是下面的方法:
override fun newCall(request: Request): Call = RealCall(this = OkHttpClient, request, forWebSocket = false)
可以看到,这里是直接创建了RealCall的对象:
class RealCall(
val client: OkHttpClient,
/** The application's original request unadulterated by redirects or auth headers. */
val originalRequest: Request,
val forWebSocket: Boolean
) : Call{
......
}
在RealCall中保存了当前的OkHttpClient对象,原始请求originalRequest.
RealCall.enqueue(Callback)
获取到RealCall之后,接下来执行了enqueue(Callback)方法,这个方法的源码如下:
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
在这个方法中,首先通过check()方法查看当前的RealCall是否已经被执行,如果已经被执行则抛出异常。这个操作是通过executed.compareAndSet(false,true)来操作的,下面是executed变量的定义:
private val executed = AtomicBoolean()
从定义上就能看出来,这个excuted在多线程的情况下能够保证原子操作,而compareAndSet(boolean expect,boolean update)方法含义为:如果AtomicBoolean中的当前值和expect表示的值一样,就将当前的值更新为update表示的值,如果不一样,就返回false。
在上面的代码中,我们判断executed中的值是否为false,如果为false,就更新为true.由于是第一次调用这个方法,所以executed中的值为0,也就是false。
执行完这个方法之后,就表示当前的RealCall已经被执行。
所以在这里需要注意,如果当前RealCall已经被执行,那么就不能在重复执行当前Call,否则就会造成异常。
RealCall.callStart()
经过上面的判断之后,如果当前的Call没有被执行,那么接下来就调用callStart()方法继续操作,这个方法的源码如下:
private fun callStart() {
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
eventListener.callStart(this)
}
可以看到,首先会根据不同的平台信息,获取当前平台的堆栈跟踪信息,通过getStackTraceForCloseable()方法的注释可以看出,这个方法返回一个对象,此对象保存在执行这个方法时创建的堆栈跟踪,这是专门用于java.io.Closeable对象,并与LogCloseableLeak结合使用。这个方法会返回null或者一个Throwable
获取到堆栈跟踪对象之后,接下来会调用eventListener.callStart(this(Call))方法,下面是这个变量的声明:
internal val eventListener: EventListener = client.eventListenerFactory.create(this)
可以看到,这里的这个对象是通过获取的OkHttpClient中的eventListenerFfactory对象,然后调用create(Call)方法生成的,下面是在OkHttpClient中对这个变量的定义:
val eventListenerFactory: EventListener.Factory = builder.eventListenerFactory
是直接调用的OkHttpClient.Builder中的这个对象:
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
进入到EventListener中:
@JvmField
val NONE: EventListener = object : EventListener() {
}
可以看到,在这里直接创建了EventListener对象,接着返回到OkHttpClient的EventListener.NONE.asFactory()方法中,下面是asFactory的源码:
//EventListener.Factory的定义
fun interface Factory {
/**
* Creates an instance of the [EventListener] for a particular [Call]. The returned
* [EventListener] instance will be used during the lifecycle of [call].
*
* This method is invoked after [call] is created. See [OkHttpClient.newCall].
*
* **It is an error for implementations to issue any mutating operations on the [call] instance
* from this method.**
*/
fun create(call: Call): EventListener
}
//asFactory的方法定义:
fun EventListener.asFactory() = EventListener.Factory { this }
可以看到,EventListener.Factory中只有一个create(Call)方法,这个方法接收一个Call参数并返回一个EventListener,这里使用了Kotlin的扩展函数,对EventListener进行扩展,直接将当前的EventListener返回。
从上面的源码可以看出,EventListener这个类用于监控一个Call的状态,EvenetListener.Factory通过create()方法通过Call创建一个EventListener,或者说将Call绑定到一个EventListener上,所以上面的代码的执行流程为:
- 通过
EventListener.NONE创建一个EventListener - 通过扩展函数
EventListener.asFactory创建一个EventListener.Factory并在create()方法中返回当前的EventListener - 在
RealCall中调用EventListener.Factory的create(this)方法将当前的Call绑定到EventListener上,这样,Call就可以通过调用EventListener的相关方法将自己的状态暴露出去。
需要注意的是:上面的EventListener.Factory使用fun interface修饰的,暂时不清楚这个修饰符的作用,但是在使用的时候发现,这个接口不能使用lambda表达式,只能使用object: EventListener.Factory去创建,但是源码中的asFactory又是使用lambda表达式创建的~~~
client.dispatcher.enqueue(AsyncCall(Callback))
在执行完callStart()之后,接下来就会执行client.dispatcher.enqueue(AsyncCall(Callback))方法,下面是client.dispatcher方法的源码:
val dispatcher: Dispatcher = builder.dispatcher
可以看到,这里是直接获取的OkHttpClient.Builder中的对象:
internal var dispatcher: Dispatcher = Dispatcher()
这里是默认创建了一个Dispatcher()对象,当然我们也可以通过OkHttpClient.Builder.dispatcher(Dispatcher)传递自己创建的Dispatcher.
获取到Dispatcher对象后,接下来是创建了AsyncCall(Callback)对象,首先查看这个对象:
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
//省略类的内容
}
这是RealCall的内部类,实现了Runnable()接口,那我们就大体可以了解,将会在工作线程中使用这个类。
创建完AsyncCall()的对象后,接下来就会调用Dispatcher.enqueue(AsyncCall)方法,下面是这个方法的源码:
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.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
可以看到,这里首先使用了同步代码块,防止多线程引起的安全问题。然后将上一步创建的AsyncCall加入到readyAsyncCalls中,这是一个双端队列,除了这个之外,在这个类中还声明了另外两个双端队列:
/** 准备异步调用的Call,按照这个容器中的顺序调用 */
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
/** 正在运行中的异步Call,包括已经取消但是还没有结束的Call */
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
/** 正在运行中的同步Call,包括已经取消但是还没结束的Call */
private val runningSyncCalls = ArrayDeque<RealCall>()
接下来判断当前的AsyncCall是否为forWebSocket,如下代码所示:
//判断call是否为forWebSocket if (!call.call.forWebSocket)
//第一个call为我们传递进来的AsyncCall
//call.call为AsyncCall中的call
val call: RealCall
get() = this@RealCall
//可以看到,其实就是RealCall,那么forwebSocket也就是RealCall中的forWebSocket,我们在创建`RealCall`的时候就指定了这个
//参数为false,所以这里就满足条件,会执行下面的
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
接下来通过findExistingCallWithHost(AsyncCall.host)来查询是否已经有存在的AsyncCall。
AsyncCall.host的源码如下:
val host: String
get() = originalRequest.url.host
可以看到,直接获取的是originalRequest.url.host,而originalRequest正式我们创建RealCall的时候传递的Request。下面是findExsitingCallWithHost(call.host):
private fun findExistingCallWithHost(host: String): AsyncCall? {
for (existingCall in runningAsyncCalls) {
if (existingCall.host == host) return existingCall
}
for (existingCall in readyAsyncCalls) {
if (existingCall.host == host) return existingCall
}
return null
}
可以看到,这里就是从上面的两个双向队列中查找,查找的方式就是遍历查询是否有host一致的数据,由于我们一开始就将需要请求的AsyncCall加入到了readyAsyncCalls中,所以这里应该至少会查找到它自己。(这里我个人的理解是:在当前AsyncCall加入到队列之前,可能已经有其它的相同host的AsyncCall被加入进去了,但是当前的AsyncCall是被加入到队列的尾部,而遍历的时候是从队列的头部开始,所以这里应该能够最先找到之前加入的AsyncCall。)
接下来,如果上一步查询到已经存在的AsyncCall,则会调用AsyncCall.reuseCallsPerHostFrom(existingCall)方法重用已经存在的AsyncCall,这个方法的源码如下:
@Volatile var callsPerHost = AtomicInteger(0)
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
可以看到,这里就是设置了callsPerHost参数的值为查找到的callsPerHost的值。(在上一步遍历查找上一个相同host的AsyncCall的时候,很有可能之前就没有相同host的AsyncCall,那么遍历完成之后就会查到它自己,这样设置callsPerHost的时候其实就是设置的自己的callsPerHost,那么在后面有和当前的AsyncCall相同host的AsyncCall加入的时候,同样会执行遍历,此时就会查找到当前的AsyncCall,就可以直接获取当前AsyncCall的callsPerHost这个参数的值。)
另外需要注意的是:这里的数据是AtomicInteger,也就是说这里保存的其实是数量,具有相同host的数量。
promoteAndExecute()
在设置完AsyncCall的callsPerHost参数后,接下来就会执行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 // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
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
}
在这个方法中,会遍历等待中的异步集合中的元素,判断是否要执行,判断条件为:
- 如果当前执行中的队列中的元素的数量超过允许请求的最大数量
maxRequests,这个值默认为64,我们可以自己设置这个值的大小。 - 如果当前请求的
host已经有至少5个相同host的请求在执行了,那么当前的请求也不会执行
如果经过上面的判断,发现当前的请求可以执行,那么就会将这个请求从readyAsyncCalls中移除,然后对和这个请求具有相同host的请求的callsPerHost加1,然后将当前请求加入到executableCalls和runningAsyncCalls中。
接下来会通过调用runningCallsCount()方法判断是否有请求正在执行:
fun runningCallsCount(): Int = runningAsyncCalls.size + runningSyncCalls.size
可以看到,这个方法通过判断同步队列和异步队列集合中的元素大小来判断是否有请求正在执行。
接下来会通过遍历executableCalls集合,调用其中每一个元素(AsyncCall)的executeOn(executorService)方法来执行请求。
executorService
在上一步中发现,我们会发现最终会使用线程池来执行异步任务,对于executorService的定义如下:
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!!
}
可以看到,这里会首先判断executorServiceOrNull是否为空,如果不为空,就直接使用这个ExecutorService,如果为空,则会创建一个ExecutorService,这个新的线程池中的核心线程数为0,允许存在的最大线程数为Int.MAX_VALUE,使用SynchronousQueue这个队列来保存线程,通过threadFactory()方法生成线程。
同时,我们也可以在创建OkHttpClient的时候设置自己的线程池,通过调用Dispatcher(ExcutorService)构造函数传入自己的线程池,下面是Dispatcher这个构造函数的源码:
constructor(executorService: ExecutorService) : this() {
this.executorServiceOrNull = executorService
}
可以看到,会将我们传入的线程池对象设置给this.excutorServiceOrNull变量,这样我们在调用excutorService变量的时候由于excutorServiceOrNull变量不为空,就会直接使用我们传入的线程池。
AsyncCall中的executeOn(ExecutorService)
经过上面的流程,我们已经创建了线程池,并且通过调用AsyncCall中的executeOn(ExcutorService)准备执行当前请求,下面是这个方法的源码:
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)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
在这个方法中,首先创建了一个变量用于保存当前的请求是否成功,默认为false,然后执行try语句块中的内容,在这里就是直接调用了excutorService.excute(this)方法,因为AsyncCall类本身实现了Runnable接口,所以这里执行这个方法就会执行AsyncCall中的run()方法。
AsyncCall.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)
}
}
}
在上面的方法中,首先会执行threadName(name,block)方法,这个方法的源码如下:
inline fun threadName(name: String, block: () -> Unit) {
val currentThread = Thread.currentThread()
val oldName = currentThread.name
currentThread.name = name
try {
block()
} finally {
currentThread.name = oldName
}
}
这个方法的作用就是对当前线程进行命名,由于线程池内的线程可能是复用的,所以这里会在当前线程执行过程中设置一个新的名字,执行完成后又设置回原来的名字。
getResponseWithInterceptorChain()
在上面的代码执行完成后,接下来会执行getResponseWithInterceptorChain(),这个方法会返回一个Response,也就是说在这里才开始真正执行网络请求,这个方法的源码如下:
@Throws(IOException::class)
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)
}
}
}
在上面的方法中,首先创建了一个列表来存放所有的Interceptor,首先获取到的是OkHttpClient中的Interceptor列表,接下来会将系统已经定义好的Interceptor加入进去。最后调用RealInterceptorChain(),接下来会执行RealChain.process(originalRequest),从而获取最终的Response,之后,如果当前请求没有取消,则会将当前请求到的Response返回,之后会调用finally语句块中,执行noMoreExchanges(null)方法。
获取到请求结束的Response后,返回之前的run()方法中,接着就会调用responseCallback(RealCall,response)方法将数据回调到我们之前传递的Callback中。最后会执行finally语句块中的client.dispatcher.finished(this)代码,finished()方法的源码如下:
internal fun finished(call: AsyncCall) {
call.callsPerHost.decrementAndGet()
finished(runningAsyncCalls, 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()
}
}
从方法名就可以看出,这个方法在请求完成后调用,在这里会首先将当前的AsyncCall从正在执行的请求队列中删除,然后判断是否还有请求正在执行,如果没有请求正在执行并且设置了idleCallback参数,那么就会执行idleCallback.run(),idleCallback是一个Runnable类型的变量,我们可以设置这个变量,用于在全部请求结束后执行这个Runnable,做一些操作。
总结
至此,对于OkHttp的基本请求流程就有了比较清除的了解,主要学习了OkHttpClient的创建;以及从调用OkHttpClient.newCall().enqueue(Callback)到最终获取到Response中间都经历了什么;EventListener在其中扮演了什么样的角色;如何设置使用自己项目的线程池这些。对于后续如何处理Interceptor将会在下一篇笔记中学习。