OkHttp请求源码分析

OkHttp分为异步请求和同步请求两部分

同步请求

vbscript 复制代码
call.execute()

execute代码如下:

#okhttp3.internal.connection.RealCall

kotlin 复制代码
override fun execute(): Response {
  check(executed.compareAndSet(false, true)) { "Already Executed" }
   
   // 1.开启超时检测
timeout.enter()
  
  callStart()
  try {
      // 2. 将请求添加在同步请求队列
    client.dispatcher.executed(this)
    //3. 调用getResponseWithInterceptorChain获取Response
    return getResponseWithInterceptorChain()
  } finally {
    client.dispatcher.finished(this)
  }
}
  1. 开启超时检测

  2. 将请求添加在同步请求队列

  3. 调用getResponseWithInterceptorChain获取Response

异步请求

okHttpClient.newCall返回值

首先,我们看一下okHttpClient.newCall返回的是什么

vbscript 复制代码
newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

newCall返回一个RealCall对象

call.enqueue的源码

kotlin 复制代码
override fun enqueue(responseCallback: Callback) {
  check(executed.compareAndSet(false, true)) { "Already Executed" }

   //1.  
callStart()
  //2.
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}
  1. callStart()方法,内部调用的EventListener.callStart,是一个全局监听请求过程的方法
  2. 调用dispatcherenqueue方法,从字面理解为,请求需要排队执行,这里的请求是AsyncCall对象,AsyncCall中包含返回结果的监听responseCallback,也就是我们在调用call.enqueue时传入的callBack

AsyncCall下面会进行具体介绍

Dispatcher的enqueue方法

scss 复制代码
internal fun enqueue(call: AsyncCall) {
  synchronized(this) {
      //1. 
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)
    }
  }
  //2. 
promoteAndExecute()
}

使用同步锁*synchronized*防止线程同步问题:

  1. AsyncCall对象加入readyAsyncCalls这个队列中

  2. promoteAndExecute(),推动并执行,我们看一下具体干了什么

Dispatcher的promoteAndExecute方法

通过注释,我们可以很清楚的知道该函数的作用:

将合法的请求从readyAsyncCalls 队列里移到runningAsyncCalls队列里,并且用ExecutorService执行他们

kotlin 复制代码
 /**
* Promotes eligible calls from [readyAsyncCalls] to [runningAsyncCalls] and runs them on the
* executor service. Must not be called with synchronization because executing calls can call
* into user code.
*
*  @return  true if the dispatcher is currently running calls.
*/
private fun promoteAndExecute(): Boolean {
  this.assertThreadDoesntHoldLock()
   
   //1. 
  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.
       // 从readyAsyncCalls中移除
      i.remove()
      asyncCall.callsPerHost.incrementAndGet()
      //2.  添加到executableCalls/runningAsyncCalls 队列中
      executableCalls.add(asyncCall)
      runningAsyncCalls.add(asyncCall)
    }
    isRunning = runningCallsCount() > 0
  }

// Avoid resubmitting if we can't logically progress
  // particularly because RealCall handles a RejectedExecutionException
  // by executing on the same thread.
  if (executorService.isShutdown) {
      ...
  } else {
    for (i in 0 until executableCalls.size) {
    // 3. 
      val asyncCall = executableCalls[i]
      asyncCall.executeOn(executorService)
    }
  }

  return isRunning
}
  1. 声明一个AsyncCall的集合
  2. 将每个AsynCallreadyAsyncCalls中移除,添加到executableCalls/runningAsyncCalls 队列中
  3. executableCalls中取出AsynCall,调用AsyncCall.executeOn方法,并传入executorService

executorServiceExecutorService对象:

kotlin 复制代码
val executorService: ExecutorService
  get() {
    if (executorServiceOrNull == null) {
      executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
          SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
    }
    return executorServiceOrNull!!
  }
  

ExecutorService是一个没有核心线程,非核心线程数为Int最大值,线程闲置60s会被回收的线程池

AsyncCall.executeOn

kotlin 复制代码
fun executeOn(executorService: ExecutorService) {
  client.dispatcher.assertThreadDoesntHoldLock()

  var success = false
  try {
      //1. 
    executorService.execute(this)
    success = true
  } catch (e: RejectedExecutionException) {
    failRejected(e)
  } finally {
    if (!success) {
      client.dispatcher.finished(this) // This call is no longer running!
    }
  }
}

使用传入的executorService线程池执行AsyncCall任务,而AsyncCall是一个Runnable对象,直接查看其run方法

AsyncCall.run

kotlin 复制代码
override fun run() {
  threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
    // 1. 
    timeout.enter()
    try {
    // 2.
      val response = getResponseWithInterceptorChain()
      signalledCallback = true
      // 3. 
      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 {
      //4. 
        responseCallback.onFailure(this@RealCall, e)
      }
    } catch (t: Throwable) {
      cancel()
      if (!signalledCallback) {
        val canceledException = IOException("canceled due to $t")
        canceledException.addSuppressed(t)
        // 5. 
        responseCallback.onFailure(this@RealCall, canceledException)
      }
      throw t
    } finally {
      client.dispatcher.finished(this)
    }
  }
}
  1. 调用timeout.enter()开启超时检测

  2. 调用getResponseWithInterceptorChain获取响应结果

  3. 获取到response后调用responseCallback.response方法

  4. 调用responseCallback.onFailure,失通知败结果

getResponseWithInterceptorChain源码分析

blog.csdn.net/xuwb123xuwb...

ini 复制代码
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)
    }
  }
}

简单来说,getResponseWithInterceptorChain方法中,就是构建一个链式拦截器,然后发起请求,整个过程是通过拦截器的链式调用完成的,即先从左到右调用,再从右到左返回结果

暂时无法在飞书文档外展示此内容

  • RetryAndFollowUpInterceptor:负责失败重试和重定向

  • BridgeInterceptor:负责把用户请求转换为发送到服务器的请求,并把服务器的响应转化为用户需要的响应

  • CacheInterceptor:负责读取缓存、更新缓存

  • ConnectInterceptor:负责和服务器建立连接

  • CallServerInterceptor:负责向服务器发送数据,从服务器读取响应数据

addInterceptor 和 addNetworkInterceptor区别

getResponseWithInterceptorChain方法中可以看到:拦截器是通过责任链模式调用的,

addInterceptor添加的Interceptor会被添加到责任链的开端,因此:

  • 任何情况下都会首先执行

  • 不受重定向和重试机制的影响,即自定义拦截器只会被执行一次

addNetworkInterceptor添加的拦截器处在ConnectInterceptor之后,因此:

  • 如果无网连接失败,该拦截器不会被调用
  • 重定向和重试几次,该拦截器就会被调用几次
  • 允许操作中间响应,比如当请求操作发生重定向(拦截被执行了两次)或者重试等。
  • 不允许调用缓存来short-circuit (短路)这个请求。(意思就是说不能从缓存池中获取缓存对象返回给客户端,必须通过请求服务的方式获取响应,也就是Chain.proceed())
  • 可以监听数据的传输
相关推荐
Lucky me.2 天前
本地maven添加jar包
okhttp·maven·jar
少说多做3433 天前
Android 网络请求(二)OKHttp网络通信
android·网络·okhttp
小白学大数据5 天前
使用OkHttp进行HTTPS请求的Kotlin实现
爬虫·python·okhttp·https·kotlin
小白学大数据5 天前
虎扑APP数据采集:JavaScript与AJAX的结合使用
开发语言·javascript·爬虫·ajax·okhttp
柯南二号6 天前
Android okhttp 网络链接各阶段监控
android·网络·okhttp
_Soy_Milk7 天前
动态网页爬取 —— ajax 与 selenium
selenium·ajax·okhttp
陈逸轩*^_^*7 天前
AJAX笔记 (速通精华版)
笔记·ajax·okhttp
Chrison_mu7 天前
Android开发|关于Okhttp发送网络请求
android·网络·okhttp
zpjing~.~8 天前
async 和 await的使用
okhttp
白乐天_n11 天前
OkHttp网络请求框架
android·网络·okhttp