从一次请求开始,深入探索OkHttp

OkHttp是Android开发中非常常用的一个网络请求库了,它支持HTTP1、HTTP2等多种协议,是我们日常开发中非常给力的帮手。本篇文章基于OkHttp4.9.0版本代码,从OkHttp的创建开始,探究OkHttp是如何发起一次网络请求的。如果你有耐心看完,相信你会对OkHttp有了一个深刻的了解。如果觉得太长不看的话,看一下总结也是不错的呢(手动滑稽)。

OkHttpClient

​ 使用OkHttp的第一步,自然都是创建OkHttpClient了:

OkHttpClient client = new OkHttpClient();

​ 通过构造方法进入,看看这个OkHttpClient到底是什么:

constructor() : this(Builder()) //默认构造 传入Builder实例

class Builder constructor() {
    internal var dispatcher: Dispatcher = Dispatcher()//调度器
    internal var connectionPool: ConnectionPool = ConnectionPool()//连接池
    internal val interceptors: MutableList = mutableListOf()//整体流程拦截器
    internal val networkInterceptors: MutableList = 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//是否可以从HTTP重定向到HTTPS
    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES//Cookie策略,是否保存Cookie
    internal var cache: Cache? = null//缓存配置
    internal var dns: Dns = Dns.SYSTEM//Dns配置
    internal var proxy: Proxy? = null//代理配置
    internal var proxySelector: ProxySelector? = null//代理选择器
    internal var proxyAuthenticator: Authenticator = Authenticator.NONE//代理服务器认证设置
    internal var socketFactory: SocketFactory = SocketFactory.getDefault()//socket配置
    internal var sslSocketFactoryOrNull: SSLSocketFactory? = null//https socket配置
    internal var x509TrustManagerOrNull: X509TrustManager? = null
    internal var connectionSpecs: List = DEFAULT_CONNECTION_SPECS
    internal var protocols: List = DEFAULT_PROTOCOLS//支持协议配置
    internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier//域名校验
    internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT//证书链
    internal var certificateChainCleaner: CertificateChainCleaner? = null
    internal var callTimeout = 0//请求超时配置 0代表不会超时
    internal var connectTimeout = 10_000//连接超时
    internal var readTimeout = 10_000//读取超时
    internal var writeTimeout = 10_000//写入超时
    internal var pingInterval = 0//针对HTTP2和web socket的ping间隔
    internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
    internal var routeDatabase: RouteDatabase? = null
  //...

​ 可以看出OkHttpClient实例就是个配置类,当实际发起请求的时候,会采用用户设置的配置。采用了Builder模式的设计,让用户更方便的配置这一系列参数,灵活的构造较多的配置。

Request

​ 基础的请求构造类,基本使用:

Request request = new Request.Builder()
    .url(ENDPOINT)
    .build();

Request类用来描述单次请求的参数信息等,包含域名、请求方式、请求头、请求体等一系列信息。通过Builder我们可以链式调用,比较优雅的配置这一系列信息。Request类和OkHttpClient类一样,本质是一个描述对象。

Call

​ 请求创建完成,就可以调用OkHttpClient来发起一次请求了,我们需要通过OkHttpClient的实例方法来发起一次请求:

Call call = client.newCall(request);

Call是一个接口,定义如下:

interface Call : Cloneable {
  /** 返回原始的请求信息 */
  fun request(): Request
    
  /** 立即发起一次请求 同步方法 不能在主线程直接调用 */
  @Throws(IOException::class)
  fun execute(): Response

  /** 发起一次异步请求 */
  fun enqueue(responseCallback: Callback)

  /** 取消请求 */
  fun cancel()

  /** 是否被执行过 */
  fun isExecuted(): Boolean
    
  /** 是否被取消了 */
  fun isCanceled(): Boolean

  /** 请求超时配置策略 */
  fun timeout(): Timeout

  /** clone这个Call */
  public override fun clone(): Call

  fun interface Factory {
    fun newCall(request: Request): Call
  }
}

OkHttpClient实现的newCall方法会创建一个RealCall实例,RealCall是应用和网络层的一个连接桥,它保存了OkHttpClient的引用和原始请求信息。我们需要通过观察它的实现来追踪网络请求流程。

enqueue

override fun enqueue(responseCallback: Callback) {
  //CAS判断是否已经被执行了
  check(executed.compareAndSet(false, true)) { "Already Executed" }
    //请求开始通知
  callStart()
  //创建异步请求入请求队列
  client.dispatcher.enqueue(AsyncCall(responseCallback))
}

​ 异步请求方法会创建一个AsyncCall,并调用OkHttpClient配置的Dispatcher处理此请求。

inner class AsyncCall(
  private val responseCallback: Callback
) : Runnable

AsyncCall实现了Runnable接口,最终会被调度器的线程池进行执行,具体后续再来分析。

execute

override fun execute(): Response {
  //CAS判断是否已经被执行了
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  timeout.enter()//请求超时计时
  callStart()//请求开始通知
  try {
    client.dispatcher.executed(this)//使用调度器加入请求队列
    return getResponseWithInterceptorChain()//请求责任链创建
  } finally {
    client.dispatcher.finished(this)//调度器结束请求
  }
}

​ 调用了execute之后请求会被加入同步请求队列,然后创建响应责任链发起请求。请求完成会从调度器中移除本次请求。

getResponseWithInterceptorChain

​ 重点来了,OkHttp发起一次请求都需要进行的方法,代码如下:

@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
  val interceptors = mutableListOf()
  interceptors += client.interceptors//用户配置的拦截器
  interceptors += RetryAndFollowUpInterceptor(client)//重连 重定向拦截器
  interceptors += BridgeInterceptor(client.cookieJar)//构建请求和响应基本信息
  interceptors += CacheInterceptor(client.cache)//缓存配置处理
  interceptors += ConnectInterceptor//连接拦截器 这里真正开始发起连接
  if (!forWebSocket) {
    interceptors += client.networkInterceptors//网络拦截器
  }
  //执行流操作(写出请求体、获得响应数据) 负责向服务器发送请求数据、从服务器读取响应数据
  //进行http请求报文的封装与请求报文的解析
  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方法会按一定的顺序构建拦截器列表,这里用到了责任链模式,处理完拦截器列表后,会创建拦截器责任链,拦截器会按顺序依次调用,处理完成之后,再将返回信息返回给用户。

cancel

override fun cancel() {
  if (canceled) return // 已经被取消 则返回

  canceled = true
  exchange?.cancel()//取消io操作
  connectionToCancel?.cancel()//关闭socket连接

  eventListener.canceled(this)//事件通知
}

​ 一次请求的取消其实就是取消了后续的IO操作和断开连接,然后进行事件通知。因为调用此方法的时候连接和IO可能还未开始,所以需要进行判空。

RealInterceptorChain

​ 通过追踪一次同步请求的发起,我们会发现最终会创建一个RealInterceptorChain实例,并调用了其proceed方法,接下来就来追踪其代码,看看内部到底是如何实现的。

@Throws(IOException::class)
override fun proceed(request: Request): Response {
  check(index < interceptors.size)//检查下标越界

  calls++

  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"
    }
  }

  // 下一个需要执行的拦截器,index+1
  val next = copy(index = index + 1, request = request)
  val interceptor = interceptors[index]
    //调用拦截器的intercept方法,传入下一个责任链
  @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
}

proceed的方法也不复杂,里面有一系列的检测方法,核心代码其实只有几行,大致逻辑如下:

  • 1、数组下标+1,取出下一个拦截器,然后复制并创建新的责任链
  • 2、获取当前下标的拦截器
  • 3、调用当前拦截器的intercept方法,并传入下一个拦截器责任链实例

    为什么可以链式调用下去呢?这里可以看一下Interceptor的接口定义

    fun interface Interceptor {
      @Throws(IOException::class)
      fun intercept(chain: Chain): Response
    }
    

    Interceptor只有一个方法,实现了intercept方法后需要调用传递进来的Chain,上面我们已经知道了这是下一个拦截器。调用了chain.proceed方法返回Response,将逻辑交由下一个拦截器处理。

Dispatcher

​ 再回过头看异步请求,上面我们可以知道,一次异步请求最终是调用了dispatcher.enqueue的方法,那么Dispatcher负责了什么呢?

Dispatcher主要负责异步请求的执行逻辑。Dispatcher中可以定义maxRequests来管理最大并发请求数量,maxRequestsPerHost来确定单个host的最大并发请求数量。

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) {
      //找到此host存在的其他call
      val existingCall = findExistingCallWithHost(call.host)
      //如果找到了 复用其他call的计数器
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
    }
  }
  //实际的去执行
  promoteAndExecute()
}

​ 调用了enqueue方法后,会先上锁,然后在异步队列readyAsyncCalls中加入此请求,再检查当前请求的host有无其他call,找到了,则复用其他call的请求计数器。最后走到promoteAndExecute去执行。

private fun promoteAndExecute(): Boolean {
  this.assertThreadDoesntHoldLock()

  val executableCalls = mutableListOf()
  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 //单个host请求上限,跳过此请求

      i.remove()
      asyncCall.callsPerHost.incrementAndGet()//cas 计数
      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
}

promoteAndExecute方法会遍历异步请求队列,如果当前并发请求数量上限了,则会跳出,不执行任何请求。如果一个host的并发请求数量达到了上限,会跳过此请求。最后,为可以执行的请求进行调用。如果用户没有自行设置线程池,则Dispatcher内部会创建一个的线程池用来执行异步网络请求。

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) {
      //请求失败了也要通知dispatcher
      client.dispatcher.finished(this) // This call is no longer running!
    }
  }
}

​ 上面我也说过了,AsyncCall本身实现了Runable接口,这里被执行之后,会调用run方法,执行内部逻辑,具体逻辑和同步请求的逻辑基本一致,这里就不再赘述。请求完成后,不管结果成功失败,都会调用Dispatcherfinished方法。

internal fun finished(call: AsyncCall) {
  call.callsPerHost.decrementAndGet()//cas 计数
  finished(runningAsyncCalls, call)
}

private fun  finished(calls: Deque, 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方法被调用后会从请求队列中移除当前请求,再尝试执行剩余的请求。Dispatcher内部也维护了同步请求队列,当同步请求完成之后也会走类似的逻辑。

RetryAndFollowUpInterceptor

​ 这个拦截器用来进行错误重试和重定向。拦截器内部是一个死循环。

try {
  response = realChain.proceed(request)
  newExchangeFinder = true
} catch (e: RouteException) {
  // The attempt to connect via a route failed. The request will not have been sent.
  if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
    throw e.firstConnectException.withSuppressed(recoveredFailures)
  } else {
    recoveredFailures += e.firstConnectException
  }
  newExchangeFinder = false
  continue
} catch (e: IOException) {
  // An attempt to communicate with a server failed. The request may have been sent.
  if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
    throw e.withSuppressed(recoveredFailures)
  } else {
    recoveredFailures += e
  }
  newExchangeFinder = false
  continue
}

​ 网络请求的异常会被catch,然后会判断是否要重新进行请求。如果能正常走下去,则会对重定向相关进行判断,创建对应的请求。

ExchangeFinder

​ 这个类在RetryAndFollowUpInterceptor中调用call.enterNetworkInterceptorExchange(request, newExchangeFinder)后被创建。这个类用来在RealConnectionPool连接池中找到一个当前请求可用的RealConnection,然后开启连接,进行接下来的IO操作。

ConnectInterceptor

​ 这个拦截器会对指定的服务器打开连接,然后执行其他的拦截器

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  val exchange = realChain.call.initExchange(chain)//初始化Exchange
  val connectedChain = realChain.copy(exchange = exchange)//为之后的责任链传入Exchange
  return connectedChain.proceed(realChain.request)
}

​ 这个拦截器会调用RealCallinitExchange方法,并把当前的责任链传递过过去。

internal fun initExchange(chain: RealInterceptorChain): Exchange {
  synchronized(this) {
    check(expectMoreExchanges) { "released" }
    check(!responseBodyOpen)
    check(!requestBodyOpen)
  }

  val exchangeFinder = this.exchangeFinder!!
  //用之前RetryAndFollowUpInterceptor传入的finder寻找编码器
  val codec = exchangeFinder.find(client, chain)
  //采用对应的编码器创建Exchange
  val result = Exchange(this, eventListener, exchangeFinder, codec)
  this.interceptorScopedExchange = result
  this.exchange = result
  synchronized(this) {
    this.requestBodyOpen = true
    this.responseBodyOpen = true
  }

  if (canceled) throw IOException("Canceled")
  return result
}

initExchange里会使用ExchangeFinder来寻找一个ExchangeCodec,这是一个网络请求的编码器,针对不同的协议会采用不同的方式来进行编码传输。 从一次请求开始,深入探索OkHttp_第1张图片

fun find(
  client: OkHttpClient,
  chain: RealInterceptorChain
): ExchangeCodec {
  try {
    //寻找一个健康的连接
    val resultConnection = findHealthyConnection(
        connectTimeout = chain.connectTimeoutMillis,
        readTimeout = chain.readTimeoutMillis,
        writeTimeout = chain.writeTimeoutMillis,
        pingIntervalMillis = client.pingIntervalMillis,
        connectionRetryEnabled = client.retryOnConnectionFailure,
        doExtensiveHealthChecks = chain.request.method != "GET"
    )
    //创建对应的编码器
    return resultConnection.newCodec(client, chain)
  } catch (e: RouteException) {
    trackFailure(e.lastConnectException)
    throw e
  } catch (e: IOException) {
    trackFailure(e)
    throw RouteException(e)
  }
}

ExchangeFinder的find方法会去尝试找到一个与服务器之间的连接。追踪findHealthyConnection代码我们会发现它内部是一个死循环,不断的调用findConnection方法去寻找一个可用的连接。findConnection的代码就比较长了,这里就不贴出来了。大概的逻辑就是优先从连接池中找连接,如果没有找到可用的连接,则会创建一个RealConnection对象,存入缓存池中。

RealConnection

RealConnection是OkHttp实际建立连接的地方。通过connect方法建立与服务器的链接。通过追踪源码我们会发现RealConnection底层还是通过Socket建立连接的。

@Throws(IOException::class)
private fun connectSocket(
  connectTimeout: Int,
  readTimeout: Int,
  call: Call,
  eventListener: EventListener
) {
  val proxy = route.proxy
  val address = route.address

  val rawSocket = when (proxy.type()) {
    Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
    else -> Socket(proxy)
  }
  this.rawSocket = rawSocket

  eventListener.connectStart(call, route.socketAddress, proxy)
  rawSocket.soTimeout = readTimeout
  try {
    //针对不同的平台进行适配
    Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
  } catch (e: ConnectException) {
    throw ConnectException("Failed to connect to ${route.socketAddress}").apply {
      initCause(e)
    }
  }
  try {
    //使用OkIO开启io
    source = rawSocket.source().buffer()
    sink = rawSocket.sink().buffer()
  } catch (npe: NullPointerException) {
    if (npe.message == NPE_THROW_WITH_NULL) {
      throw IOException(npe)
    }
  }
}

CallServerInterceptor

​ 这是所有拦截器中的最后一个拦截器。在这个拦截器里会进行IO操作与服务器交互。OkHttp底层使用了OkIO来进行IO操作。

@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
  val realChain = chain as RealInterceptorChain
  val exchange = realChain.exchange!!//交换数据的桥梁,具体将具体交换逻辑分发到不同的实现
  val request = realChain.request
  val requestBody = request.body
  val sentRequestMillis = System.currentTimeMillis()

  var invokeStartEvent = true
  var responseBuilder: Response.Builder? = null
  var sendRequestException: IOException? = null
  try {
    //写请求头 最终会调用具体的ExchangeCodec去完成
    exchange.writeRequestHeaders(request)

    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      //遇到HTTP/1.1 中约定的Expect: 100-continue 会直接发起请求
      if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
        exchange.flushRequest()//发送请求报文
        responseBuilder = exchange.readResponseHeaders(expectContinue = true)//读取响应头
        exchange.responseHeadersStart()//事件通知
        invokeStartEvent = false
      }
      if (responseBuilder == null) {
        if (requestBody.isDuplex()) {
             //针对HTTP2协议的双工请求体 先发送请求头
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)//写入请求体
        } else {
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)//写入请求体
          bufferedRequestBody.close()
        }
      } else {
        //没有请求体
        exchange.noRequestBody()
        if (!exchange.connection.isMultiplexed) {
          exchange.noNewExchangesOnConnection()
        }
      }
    } else {
      //没有请求体
      exchange.noRequestBody()
    }

    if (requestBody == null || !requestBody.isDuplex()) {
      exchange.finishRequest()//结束请求写入并发送
    }
  } catch (e: IOException) {
    if (e is ConnectionShutdownException) {
      throw e // No request was sent so there's no response to read.
    }
    if (!exchange.hasFailure) {
      throw e // Don't attempt to read the response; we failed to send the request.
    }
    sendRequestException = e
  }

  try {
    if (responseBuilder == null) {
      //读取响应头
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        //事件通知
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }
    //构建Response
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()
    var code = response.code
    if (code == 100) {
      // Server sent a 100-continue even though we did not request one. Try again to read the
      // actual response status.
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
      }
      //如果code 是100则重新读取
      response = responseBuilder
          .request(request)
          .handshake(exchange.connection.handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build()
      code = response.code
    }
      //事件通知
    exchange.responseHeadersEnd(response)

    response = if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response.newBuilder()
          .body(EMPTY_RESPONSE)
          .build()
    } else {
      //写入ResponseBody
      response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    }
    //收到关闭连接的Header 关闭IO
    if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
        "close".equals(response.header("Connection"), ignoreCase = true)) {
      exchange.noNewExchangesOnConnection()
    }
    if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
      throw ProtocolException(
          "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
    }
    //返回Response
    return response
  } catch (e: IOException) {
    if (sendRequestException != null) {
      sendRequestException.addSuppressed(e)
      throw sendRequestException
    }
    throw e
  }
}

CallServerInterceptorintercept方法很长,不过逻辑并不复杂。大致流程如下:

  • 1、根据Request的配置写入请求行和请求头。
  • 2、根据Method判断是否支持请求体,如果支持则尝试写入请求体并发送请求报文,否则直接发送
  • 3、读取响应报文,构建Response
  • 4、读取响应体,为Response写入ResponseBody
  • 5、判断是否要关闭连接
  • 6、返回Response

​ 因为CallServerInterceptor是最后一个Interceptor,所以返回的Response会一级一级的向上传递,最后用户就能拿到包装后的响应Response了。

BridgeInterceptor

​ 这个拦截器是应用和网络交互的一个桥梁。首先他会获取Request里的信息,根据请求内容在Request中添加或者一些请求头,这些都是用户未感知到的。同时这个拦截器还会读取Cookie配置,如果有Cookie信息,也会通过请求头带到服务端。

​ 在Request信息完善后,会调用后续的责任链去处理完善的Request,并等待后续的返回。

        val networkResponse = chain.proceed(requestBuilder.build())//等待响应

    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)//处理Cookie

    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)//将原始请求信息放入Response

    if (transparentGzip &&
        "gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
        networkResponse.promisesBody()) {
      val responseBody = networkResponse.body
      if (responseBody != null) {
        val gzipSource = GzipSource(responseBody.source())
        val strippedHeaders = networkResponse.headers.newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build()//精简响应头
        responseBuilder.headers(strippedHeaders)
        val contentType = networkResponse.header("Content-Type")
        //处理ResponseBody
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }
        //返回完善的Response
    return responseBuilder.build()

​ 收到后续的返回后,会对Response进行处理,并精简一些响应头。最终将完善的Response回传给用户。很多操作都是用户无感知的完成的,深藏功与名。

Response

​ 这就没什么好说的了吧?在经过一些列链式调用和处理,最终用户可以拿到一个Response对象。在这里用户就可以拿到请求的各种信息以及相应,剩下的就交给用户自行处理了。在用户拿到Response后,一次网络请求也算完成啦!

总结

​ 话不多说,先上图

从一次请求开始,深入探索OkHttp_第2张图片

​ 再来文字总结一下,OkHttpClinent类是一个网络请求的配置类,我们通过构建Request来描述我们的请求信息,接着使用newCall方法可以创建一个RealCall实例。

RealCall可以选择同步执行或者是异步执行,他是我们与网络交互的一个桥梁。如果是同步请求,则会直接调用gerResponseWithInterceptorChain()方法创建拦截器责任链。如果是异步请求,则会分发到DispatcherDispatcher最终会使用线程池执行这个请求,最终也还是会走到gerResponseWithInterceptorChain()方法。

​ 在gerResponseWithInterceptorChain()方法中,会将所有的拦截器按照一定顺序封装进一个列表,构建一个RealInterceptorChainproceed方法会调用各个拦截器的intercept方法,拦截器在处理完成自己的职责后,继续调用proceed方法。

RetryAndFollowUpInterceptor负责处理错误重试和重定向,BridgeInterceptor负责包装请求和返回数据,CacheInterceptor负责缓存的处理,而ConnectInterceptor则真正的打开了连接,最后通过CallServerInterceptor进行网络IO,发送和处理报文。

OkHttp底层采用了Socket进行网络连接,采用OkIO进行网络IO,有连接池逻辑,会存储RealConnection实例,减少过多连接产生的开销。

​ 阅读源码不难,难的是写源码时候应该具备的思想。通过阅读OkHttp源码可以让我们学习到很多设计思想:通过Builder模式构建复杂对象、通过责任链模式有序的分发任务和回传、功能的抽象等等。好的开源框架的源码阅读真的能让我们收获很多,我们应该学以致用,这样就算有一天别人让你做一个类似的功能时候,你也不会毫无头绪。希望这篇文章能让你有所收获,当然我更建议你自己阅读一遍源码,也许会有不一样的收获。

能看到这里也不容易,顺手给个赞吧,欢迎分享不同的见解(#^.^#)

视频:
资深架构师带你逐题详解Android大厂精选高频面试题:OkHttp的使用流程OkHttp分发器原理
OkHttp分发器线程池上OkHttp分发器线程池下OkHttp拦截器责任链设计模式

原文: https://juejin.cn/post/6909445385266135048#comment

你可能感兴趣的:(androidokhttp)