OkHttp(4.11.0版本)的源码解析


一、OkHttp是什么?

Github的原文是:Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
即 Square为JVM、Android和GraalVM精心设计的HTTP客户端。

通过OkHttp的发展史,我们看到,最开始是Square公司觉得Android提供的HttpClient和HttpUrlConnection使用不够便捷,便开发了OkHttp,封装了HttpClient和HttpUrlConnection,后来Apache的HttpClient被摒弃了,OkHttp只保留了HttpUrlConnection,再后来Square觉得HttpUrlConnection也不好用,就摒弃了它,自行开发了HTTP链接的操作。Android官方也于Android4.4发布时,
将HttpUrlConnection的实现换成了OkHttp框架的方式。


二、简单使用步骤

OkHttp的使用文档已经写的很清楚了,我就不花太多时间讲解使用方式。

1.引入库

implementation("com.squareup.okhttp3:okhttp:4.11.0")//OkHttp
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")//Log工具

引入OkHttp4.x 的库会自动引入Okio(一个高效的I/O操作库)和Kotlin标准库

2.网络请求

//配置OkHttpClient
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(HttpLoggingInterceptor())//添加日志拦截器
    .build()
//配置Request
val request = Request.Builder()
    .url("https://www.wanandroid.com/article/list/0/json")
    .build()
//进行请求
okHttpClient.newCall(request)
    .enqueue(object : Callback{
        override fun onFailure(call: Call, e: IOException) {
			//请求失败信息
        }
        override fun onResponse(call: Call, response: Response) {
			//返回的Response            
        }
    })

三、源码分析

通过API我们知道,进行接口访问的方法是通过Call的enqueue()进行异步请求或者execute()进行同步请求,Call的实现类是RealCall,我们先从enqueue()作为突破口。

RealCall.class
  
  //异步请求
  override fun enqueue(responseCallback: Callback) {
    check(executed.compareAndSet(false, true)) { "Already Executed" }
    callStart()
    //调用了Dispatcher的enqueue()
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

进入Dispatcher

Dispatcher.class,起到线程调度的作用,本质是利用Java的Executor,此处就不详细介绍多线程的知识了。

  //异步请求入口
  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()
  }

  //执行队列中的请求
  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()
        //判断正在请求的链接数是否超过最大值,超过后直接break,剩余的call需要等待
        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        //判断对同一主机的链接是否超过最大值,超过后continue下一个call
        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
  }

进入AsyncCall

AsyncCall.class,实现了Runnable接口

   fun executeOn(executorService: ExecutorService) {
	 ......
     try {
       //线程池执行this,即执行AsyncCall的run方法
       executorService.execute(this)
       success = true
     } catch (e: RejectedExecutionException) {
       ......
     }
   }

   override fun run() {
      threadName("OkHttp ${redactedUrl()}") {
        ......
        try {
          //OkHttp的最关键方法,即请求方法
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          responseCallback.onResponse(this@RealCall, response)
        } catch (e: IOException) {
          ......
        }
      }
    }

以上是通过Call的enqueue()方法异步调用了RealCall的getResponseWithInterceptorChain(),其实我们如果以execute()作为突破口的话,会发现也是进入这个方法。

RealCall.class
  //同步请求入口
  override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" }
    timeout.enter()
    callStart()
    try {
      client.dispatcher.executed(this)
      //OkHttp的最关键方法,即请求方法
      return getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }

现在我们就看一下getResponseWithInterceptorChain()到底是如何进行网络请求的。

RealCall.class

  @Throws(IOException::class)
  internal fun getResponseWithInterceptorChain(): Response {
    //第一部分:按照顺序整合所有的Interceptor
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors //自己添加的Interceptor
    interceptors += RetryAndFollowUpInterceptor(client) //重试和重定向操作
    interceptors += BridgeInterceptor(client.cookieJar) //拼接报文信息的操作
    interceptors += CacheInterceptor(client.cache) //缓存相关操作
    interceptors += ConnectInterceptor //建立网络连接的操作
    if (!forWebSocket) {
      //针对网络层的自己添加的Interceptor,一般在调试接口时使用,可以拿到完整的请求报文和返回报文
      interceptors += client.networkInterceptors 
    }
    interceptors += CallServerInterceptor(forWebSocket) //建立网络请求的操作
	//第二部分:生成RealInterceptorChain对象
    val chain = RealInterceptorChain(
        call = this,
        interceptors = interceptors,
        index = 0,
        exchange = null,
        request = originalRequest,
        connectTimeoutMillis = client.connectTimeoutMillis,
        readTimeoutMillis = client.readTimeoutMillis,
        writeTimeoutMillis = client.writeTimeoutMillis
    )
	//第三部分:执行Chain的proceed()方法,开始链式调用各个Interceptor,从自己添加的Interceptors
	//(如果没添加默认从RetryAndFollowUpInterceptor)开始,直到CallServerInterceptor进行网络请求,
	//再一步一步返回到第一个Interceptor,最后返回Response
	//通过责任链模式,实现了完整的从 网络链接->网络请求->请求返回 的全过程
    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情况下,OkHttp是如何实现完整网络请求的
按照顺序首先是RetryAndFollowUpInterceptor.class,

RetryAndFollowUpInterceptor.class

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    var request = chain.request
    val call = realChain.call
    var followUpCount = 0
    var priorResponse: Response? = null
    var newExchangeFinder = true
    var recoveredFailures = listOf<IOException>()
    while (true) { //开启循环处理重试和重定向问题,直到返回数据或者遇到其他错误return
      //做链接准备,根据newExchangeFinder判断是否需要创建ExchangeFinder,
      //ExchangeFinder是用来寻找合适的交换链接的,ConnectInterceptor会用到
      call.enterNetworkInterceptorExchange(request, newExchangeFinder)
      var response: Response
      var closeActiveExchange = true
      try {
        if (call.isCanceled()) {
          throw IOException("Canceled")
        }
        try {
          //链式调用下一个Interceptor
          response = realChain.proceed(request)
          newExchangeFinder = true
        } catch (e: RouteException) {
          //处理某一条链接线路出错的情况,判断是否可重试
          //因为当以域名进行请求的时候,可能DNS解析到多个ip,还因为可能存在Proxy,所以会有多条链接线路出现
          // 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) {
          //处理IO异常,例如请求超时、HTTP协议出错、SSL认证出错等等,判断是否可重试
          // 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
        }
        
        //以下操作是访问没有出错,但是可能出现了重定向
        // Attach the prior response if it exists. Such responses never have a body.
        if (priorResponse != null) {
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
              .build()
        }

        val exchange = call.interceptorScopedExchange
        
	    /**
	     * 依据各种HTTP状态码判断是否需要重定向
	     *
	     * 此方法不仅处理了30X系列code,还处理了
	     * 401-Unauthorized、 407-Proxy Authentication Required、 408-Request Time-Out、
	     * 421-HTTP2中使用相同证书子域名 SSL配置不一致导致、503-Service Unavailable
	     *
	     * 1. 针对401、407,需要用户配置了OkHttpClient的authenticator(授权认证)、proxyAuthenticator(代理认证)
	     * 2. 针对30X系列的判断条件(解释一下,307类似302,但不允许重定向为GET,308类似301,也不允许重定向为GET)
	     *    a.用户设置可以重定向
	     *    b.response中header的"location"可以解析
	     *    c.支持重定向地址的协议
	     *    d.返回scheme和请求的scheme必须一致且配置允许ssl重定向
	     *    e.针对存在请求body的,
	     *      如果可以重定向为GET且不是307,308,构建GET请求并置空RequestBody
	     *      否则如果是307或者308或者Method是PROPFIND,构建原有请求并添加RequestBody
	     *      否则构建原有请求并置空RequestBody
	     *    f.跨主机重定向时,删除所有身份验证头。
	     * 3. 针对408的判断条件
	     *    a.用户是否设置可以重定向
	     *    b.如果当前Response不为空,且只允许请求一次,不允许重定向
	     *    c.本次请求结果和上一次请求结果均超时,不允许重定向
	     *    d.解析结果相应头Retry-After(响应的 HTTP 报头指示所述用户代理应该多长时间使一个后续请求之前等待
	     *      如果当前Retry-After大于0,不允许重定向
	     * 4. 针对503的判断条件
	     *    a.本次请求结果和上一次请求结果均返回503,不允许重定向
	     *    b.如果返回的Retry-After为0,没有任何延迟,则返回Request对象,否则不允许重定向
	     * 5. 针对421(在HTTP2多路复用时,发现连接到的服务器不正确,则会由服务器返回响应码 421,客户端收到后会重新建立连接并且发送相同的请求)
	     *    a.如果当前Response不为空,且只允许请求一次,不允许重定向
	     *    b.如果exchange为空或者exchange不是多路复用的,不允许重定向
	     *    c.否则进行重定向
	     */
        val followUp = followUpRequest(response, exchange)

  		//没有重定向,放回Response
        if (followUp == null) {
          if (exchange != null && exchange.isDuplex) {
            call.timeoutEarlyExit()
          }
          closeActiveExchange = false
          return response
        }
		//如果request body只能发送一次,也返回Response,isOneShot()默认都是false,除非被子类重写
        val followUpBody = followUp.body
        if (followUpBody != null && followUpBody.isOneShot()) {
          closeActiveExchange = false
          return response
        }
        
        response.body?.closeQuietly()
  		//重定向超过20次抛出异常
        if (++followUpCount > MAX_FOLLOW_UPS) {
          throw ProtocolException("Too many follow-up requests: $followUpCount")
        }
		//用重定向的request去进行请求
        request = followUp
        //将本次的响应记录到临时变量中
        priorResponse = response
      } finally {
        call.exitNetworkInterceptorExchange(closeActiveExchange)
      }
    }
  }

  //异常是否可修复
  private fun recover(
    e: IOException,
    call: RealCall,
    userRequest: Request,
    requestSendStarted: Boolean
  ): Boolean {
    // The application layer has forbidden retries.
    if (!client.retryOnConnectionFailure) return false
    // We can't send the request body again.
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
    // This exception is fatal.
    // isRecoverable()判断了ProtocolException、SocketTimeoutException、
    // SSLHandshakeException && CertificateException、SSLPeerUnverifiedException 四种情况不可修复
    if (!isRecoverable(e, requestSendStarted)) return false
    // No more routes to attempt.
    if (!call.retryAfterFailure()) return false
    // For failure recovery, use the same route selector with a new connection.
    return true
  }

RetryAndFollowUpInterceptor
前置工作:做链接准备
中置工作:链式调用下一个Interceptor
后置工作:通过Response进行重试和重定向操作。

然后我们看下一个Interceptor:BridgeInterceptor

BridgeInterceptor.class

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val userRequest = chain.request()
    val requestBuilder = userRequest.newBuilder()

	// 前置部分:拼接Header信息,包括Content-Type、Content-Length、使用gzip等
    val body = userRequest.body
    if (body != null) {
      val contentType = body.contentType()
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString())
      }
      val contentLength = body.contentLength()
      if (contentLength != -1L) {
        requestBuilder.header("Content-Length", contentLength.toString())
        requestBuilder.removeHeader("Transfer-Encoding")
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked")
        requestBuilder.removeHeader("Content-Length")
      }
    }
    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", userRequest.url.toHostHeader())
    }
    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive")
    }
    var transparentGzip = false
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true
      requestBuilder.header("Accept-Encoding", "gzip")
    }
    val cookies = cookieJar.loadForRequest(userRequest.url)
    if (cookies.isNotEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies))
    }
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", userAgent)
    }

	//中置部分:链式调用下一个Interceptor
    val networkResponse = chain.proceed(requestBuilder.build())

	//后置部分:1.保存cookie信息 2.如果response使用了gzip,进行解析
    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
    val responseBuilder = networkResponse.newBuilder()
        .request(userRequest)
    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")
        responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
      }
    }

    return responseBuilder.build()
  }

BridgeInterceptor主要起到代码和HTTP请求的转换作用。

接下来我们看下一个:CacheInterceptor

CacheInterceptor.class

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val call = chain.call()
    //获取缓存
    val cacheCandidate = cache?.get(chain.request())

    val now = System.currentTimeMillis()
 	//获取缓存策略
 	//通过各种条件获取到CacheStrategy对象,包含2个属性networkRequest和cacheResponse
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

    cache?.trackResponse(strategy)
    val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE

    if (cacheCandidate != null && cacheResponse == null) {
      // The cache candidate wasn't applicable. Close it.
      cacheCandidate.body?.closeQuietly()
    }
	// 如果networkRequest和cacheResponse都为null,则直接返回出错
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(HTTP_GATEWAY_TIMEOUT)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build().also {
            listener.satisfactionFailure(call, it)
          }
    }

	// 如果networkRequest为空,即不需要网络请求,直接返回缓存数据
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse!!.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build().also {
            listener.cacheHit(call, it)
          }
    }

    if (cacheResponse != null) {
      listener.cacheConditionalHit(call, cacheResponse)
    } else if (cache != null) {
      listener.cacheMiss(call)
    }

    var networkResponse: Response? = null
    try {
      //networkRequest不为null,进行网络请求
      //链式调用下一个Interceptor
      networkResponse = chain.proceed(networkRequest)
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }
	//如果本身有缓存,进行网络请求后,http code是304(表示无修改),则更新缓存响应并返回
    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      if (networkResponse?.code == HTTP_NOT_MODIFIED) {
        val response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers, networkResponse.headers))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis)
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build()

        networkResponse.body!!.close()

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache!!.trackConditionalCacheHit()
        cache.update(cacheResponse, response)
        return response.also {
          listener.cacheHit(call, it)
        }
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build()
        
	// cache不为空,有请求头和缓存策略时,通过cache.put进行缓存
    if (cache != null) {
      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        val cacheRequest = cache.put(response)
        return cacheWritingResponse(cacheRequest, response).also {
          if (cacheResponse != null) {
            // This will log a conditional cache miss only.
            listener.cacheMiss(call)
          }
        }
      }
	  //如果HttpMethod判断缓存无效,清除缓存
      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }

    return response
  }

CacheInterceptor
前置工作:判断是否从缓存中返回Response
中置工作:链式调用下一个Interceptor
后置工作:判断是否需要从Response中进行缓存数据或缓存更新,只缓存GET请求的数据

然后我们看下一个Interceptor:ConnectInterceptor

ConnectInterceptor.class

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    //前置操作:获取或者生成链接
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)//调用RealCall的initExchange(后续讲解)
    val connectedChain = realChain.copy(exchange = exchange)
    //中置操作:链式调用下一个Interceptor
    return connectedChain.proceed(realChain.request)
  }
RealCall.class

  //初始化Exchange
  internal fun initExchange(chain: RealInterceptorChain): Exchange {
    ......
    //在RetryAndFollowupInterceptor中创建
    val exchangeFinder = this.exchangeFinder!! 
    //获取ExchangeCodec对象,根据Protocol分为Http1ExchangeCodec和Http2ExchangeCodec(后续讲解)
    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
  }
ExchangeFinder.class

  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"
      )
      //返回ExchangeCodec
      return resultConnection.newCodec(client, chain)
    } catch (e: RouteException) {
	  ......
    }
  }

  @Throws(IOException::class)
  private fun findHealthyConnection(
    connectTimeout: Int,
    readTimeout: Int,
    writeTimeout: Int,
    pingIntervalMillis: Int,
    connectionRetryEnabled: Boolean,
    doExtensiveHealthChecks: Boolean
  ): RealConnection {
    while (true) {
      //找到链接(后面有解析)
      val candidate = findConnection(
          connectTimeout = connectTimeout,
          readTimeout = readTimeout,
          writeTimeout = writeTimeout,
          pingIntervalMillis = pingIntervalMillis,
          connectionRetryEnabled = connectionRetryEnabled
      )
      // 判断链接是否健康
      if (candidate.isHealthy(doExtensiveHealthChecks)) {
        return candidate
      }
      // 不健康时,添加标记,以便移出pool
      candidate.noNewExchanges()
      //根据条件遍历所有Route
      if (nextRouteToTry != null) continue
      val routesLeft = routeSelection?.hasNext() ?: true
      if (routesLeft) continue
      val routesSelectionLeft = routeSelector?.hasNext() ?: true
      if (routesSelectionLeft) continue

      throw IOException("exhausted all routes") //异常,用尽所有route
    }
  }

  @Throws(IOException::class)
  private fun findConnection(
    connectTimeout: Int,
    readTimeout: Int,
    writeTimeout: Int,
    pingIntervalMillis: Int,
    connectionRetryEnabled: Boolean
  ): RealConnection {
    if (call.isCanceled()) throw IOException("Canceled")

	//尝试复用call之前的链接
    val callConnection = call.connection 
    if (callConnection != null) {
      var toClose: Socket? = null
      synchronized(callConnection) {
        //判断链接是否可复用,不可用就release
        if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
          toClose = call.releaseConnectionNoEvents()
        }
      }
      //如果没被release,复用
      if (call.connection != null) {
        check(toClose == null)
        //返回call之前使用的链接
        return callConnection
      }
      //如果release了,关闭socket
      toClose?.closeQuietly()
      eventListener.connectionReleased(call, callConnection)
    }
	
	//当前call没有可以复用的链接,后续需要去获取一个新的链接:1.从pool中获取 2.创建新的
    refusedStreamCount = 0
    connectionShutdownCount = 0
    otherFailureCount = 0
	//第一次从pool中寻找:(后续讲解)
	//尝试从链接池中获取一个链接使用,routes参数为null,即只寻找符合HTTP1的链接
    if (connectionPool.callAcquirePooledConnection(address, call, null, false)) { 
      val result = call.connection!!
      eventListener.connectionAcquired(call, result)
      //返回链接池中的可复用链接
      return result
    }
	//从pool中没有得到复用的链接,需要获取一个route对象去生成链接
    val routes: List<Route>? 
    val route: Route
    if (nextRouteToTry != null) {
      //如果之前有一个可以生成有效链接的route,使用它,避免重复选择route的操作
      routes = null
      route = nextRouteToTry!!
      nextRouteToTry = null
    } else if (routeSelection != null && routeSelection!!.hasNext()) {
      //如果有selection,且selection中还有未尝试的route, 获取一个route
      routes = null
      route = routeSelection!!.next()
    } else {
      //生成新的RouteSelector,一个RouteSelector包含多个Selection,一个Selection包含多个Route
      var localRouteSelector = routeSelector
      if (localRouteSelector == null) {
        localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
        this.routeSelector = localRouteSelector
      }
      val localRouteSelection = localRouteSelector.next()
      routeSelection = localRouteSelection
      routes = localRouteSelection.routes

      if (call.isCanceled()) throw IOException("Canceled")
	  
	  //第二次从pool中寻找
	  //此时routes不为null,可以获取符合链接复用的HTTP2的链接以及符合条件的HTTP1的链接
      if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
        val result = call.connection!!
        eventListener.connectionAcquired(call, result)
        return result
      }
      
	  //第二次从pool中仍没获取复用的链接,获取一个route,去生成新链接
      route = localRouteSelection.next()
    }

    //生成新链接对象
    val newConnection = RealConnection(connectionPool, route)
    call.connectionToCancel = newConnection //默认链接可能创建失败
    try {
      //创造链接,阻塞式的(后续讲解)
      newConnection.connect(
          connectTimeout,
          readTimeout,
          writeTimeout,
          pingIntervalMillis,
          connectionRetryEnabled,
          call,
          eventListener
      )
    } finally {
      call.connectionToCancel = null //没出错说明创建了有效链接,将connectionToCancel置为null
    }
    //将能生成有效链接的route从失败的list中移除
    call.client.routeDatabase.connected(newConnection.route())
    
	//第三次从pool种寻找:只拿多路复用的链接
	//如果在创建完链接时发现有多路复用的HTTP2链接,复用,并关闭刚创建的HTTP2链接
    if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
      val result = call.connection!!
      //如果拿到可复用的链接,缓存当前route,因为当前route可以创建有效链接,避免下一次再重复寻找route
      nextRouteToTry = route
      newConnection.socket().closeQuietly()
      eventListener.connectionAcquired(call, result)
      //返回复用链接
      return result
    }
	//第三次从链接池仍没有找到可复用的链接,将新建的链接放入链接池,并将链接赋值给当前call
    synchronized(newConnection) {
      connectionPool.put(newConnection)
      call.acquireConnectionNoEvents(newConnection)
    }
    eventListener.connectionAcquired(call, newConnection)
    //返回新链接
    return newConnection
  }

RealConnectionPoll.class

  //从链接池获取符合要求的链接
  fun callAcquirePooledConnection(
    address: Address,
    call: RealCall,
    routes: List<Route>?,
    requireMultiplexed: Boolean
  ): Boolean {
    for (connection in connections) {
      synchronized(connection) {
        //HTTP2多路复用判断
        if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
        //链接是否合格,主要判断内容:1.链接数没超过上限 2.连接方式要一致
        if (!connection.isEligible(address, routes)) return@synchronized
        //复用链接
        call.acquireConnectionNoEvents(connection)
        return true
      }
    }
    return false
  }
RealConnection.class

  //链接是否可用
  internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
    assertThreadHoldsLock()
	//链接中的call数量超标或不许添加新请求时,不合格
	//HTTP1中allocationLimit为1,HTTP2中okhttp设置是4
    if (calls.size >= allocationLimit || noNewExchanges) return false
	//如果除了host之外的数据有不同的,不合格,包括如下参数:
	//this.proxyAuthenticator
    //this.protocols
    //this.connectionSpecs
    //this.proxySelector
    //this.proxy
    //this.sslSocketFactory
    //this.hostnameVerifier
    //this.certificatePinner
    //this.url.port
    if (!this.route.address.equalsNonHost(address)) return false
    //如果host相同,合格
    if (address.url.host == this.route().address.url.host) {
      return true // This connection is a perfect match.
    }

    //如果Host不相同,判断是否是HTTP2的链接
    if (http2Connection == null) return false	
    // route必须是同样的address,包括ip和port
    if (routes == null || !routeMatchesAny(routes)) return false
    // HostnameVerifier要一样,且已建立链接的证书支持当前url
    if (address.hostnameVerifier !== OkHostnameVerifier) return false
    if (!supportsUrl(address.url)) return false
    // CertificatePinner也要支持当前host
    try {
      address.certificatePinner!!.check(address.url.host, handshake()!!.peerCertificates)
    } catch (_: SSLPeerUnverifiedException) {
      return false
    }
    //符合复用要求
    return true // The caller's address can be carried by this connection.
  }

  //真正建立链接
  fun connect(
    connectTimeout: Int,
    readTimeout: Int,
    writeTimeout: Int,
    pingIntervalMillis: Int,
    connectionRetryEnabled: Boolean,
    call: Call,
    eventListener: EventListener
  ) {
    check(protocol == null) { "already connected" }
    var routeException: RouteException? = null
    val connectionSpecs = route.address.connectionSpecs
    val connectionSpecSelector = ConnectionSpecSelector(connectionSpecs)
	......
    while (true) {
      try {
        //使用HttpTunnel,用Proxy将HTTP代理为HTTPS
        if (route.requiresTunnel()) {
          //创建HttpTunnel链接
          connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
          if (rawSocket == null) {
            // We were unable to connect the tunnel but properly closed down our resources.
            break
          }
        } else {
          //创建TCP Socket链接
          connectSocket(connectTimeout, readTimeout, call, eventListener)
        }
        //依据协议判断是否需要开启HTTP2、是否建立TLS链接
        establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
        eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
        break
      } catch (e: IOException) {
        ......
      }
    }
  }

ConnectInterceptor
前置工作:获取或者创建一个可用的链接,一共五步

  1. 判断当前call是否有可用的connection,有就返回
  2. 从pool中获取符合HTTP1的可复用链接,有就返回
  3. 从pool中获取符合HTTP1和HTTP2的可复用链接,有就返回
  4. 根据route创建一个新的connection
  5. 从pool中获取符合HTTP2的可复用链接,有就返回,没有则返回第4步创建的connection

中置工作:链式调用下一个Interceptor
后置工作:无

然后我们看最后一个CallServerInterceptor

CallServerInterceptor.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 {
      //写入Request的Header
      exchange.writeRequestHeaders(request)
	  //判断是否需要写入RequestBody
      if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
        // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
        // Continue" response before transmitting the request body. If we don't get that, return
        // what we did get (such as a 4xx response) without ever transmitting the request body.
        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()) {
            // Prepare a duplex body so that the application can send a request body later.
            exchange.flushRequest()
            val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
            requestBody.writeTo(bufferedRequestBody)
          } else {
            // Write the request body if the "Expect: 100-continue" expectation was met.
            val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
            requestBody.writeTo(bufferedRequestBody)
            bufferedRequestBody.close()
          }
        } else {
          exchange.noRequestBody()
          if (!exchange.connection.isMultiplexed) {
            // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
            // from being reused. Otherwise we're still obligated to transmit the request body to
            // leave the connection in a consistent state.
            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
    }
    //下面是解析Response的代码,也是利用ExchangeCodec对象
    try {
      if (responseBuilder == null) {
        responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
        if (invokeStartEvent) {
          exchange.responseHeadersStart()
          invokeStartEvent = false
        }
      }
      var response = responseBuilder
          .request(request)
          .handshake(exchange.connection.handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build()
      var code = response.code

      if (shouldIgnoreAndWaitForRealResponse(code)) {
        responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
        if (invokeStartEvent) {
          exchange.responseHeadersStart()
        }
        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 {
        response.newBuilder()
            .body(exchange.openResponseBody(response))
            .build()
      }
      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,倒序执行各个Interceptor的后置工作。
      return response
    } catch (e: IOException) {
      if (sendRequestException != null) {
        sendRequestException.addSuppressed(e)
        throw sendRequestException
      }
      throw e
    }
  }

CallServerInterceptor的
前置工作:Encodes HTTP requests
中置工作:无,网络请求是最后一步
后置工作:Decodes HTTP responses

CallServerInterceptor基于Exchange中的ExchangeCodec对象(细分为HTTP1ExchangeCodec和HTTP2ExchangeCodec)来最终实现网络请求的,I/O部分是用的Okio框架(在这里就不分析了)。

至此,基于OkHttp五大拦截器的网络请求过程全部讲完了。

总结

  1. 使用OkHttp框架的优势、好处有哪些
    a. 自带重试和重定向处理
    b. 内置链接池,支持复用,且支持HTTP2,可以使用多路复用
    c. 使用Okio,高效的I/O操作
    d. http2.0协议支持对Header压缩。okhttp提供了Gzip压缩body体
    e. 实现了请求的缓存处理

  2. OkHttp中使用了哪些设计模式可以借鉴学习
    a. 构造者模式:Request 和 Response 使用了 Builder 模式来创建,这样可以很方便地设置各种参数,并且保持了对象的不可变性。
    b. 责任链模式:通过设置多个拦截器Interceptor,将网络请求细分为多个步骤,每个步骤实现一部分功能,每个半部分都能处理请求和响应。
    c. 观察者模式:网络请求使用Callback,当异步请求完成时,注册的观察者会收到通知,并执行相应的回调。

如果对Retrofit框架有兴趣的朋友可以看一下我的另一篇文章,介绍了基于OkHttp的Retrofit框架的使用和源码分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
OkHttp3是一个开源的Java HTTP客户端库,它被广泛用于Android和Java应用程序中进行网络请求。它提供了简洁的API和强大的功能,使得进行HTTP通信变得更加简单和高效。 以下是OkHttp3的一些主要特点和功能: 1. 支持同步和异步请求:OkHttp3允许您发送同步和异步的HTTP请求。同步请求会阻塞当前线程,直到请求完成并返回响应。异步请求则会在后台线程执行,不会阻塞主线程。 2. 请求和响应拦截器:OkHttp3提供了拦截器机制,可以在发送请求和接收响应的过程中进行自定义操作。您可以使用拦截器来添加、修改或删除请求头、记录日志、进行身份验证等。 3. 连接池和连接复用:OkHttp3使用连接池来管理HTTP连接,以减少网络延迟和提高性能。它还支持连接复用,可以在多个请求之间共享同一个TCP连接,减少连接建立的开销。 4. 支持HTTP/2和WebSocket:OkHttp3支持HTTP/2协议,可以通过单个TCP连接并发地发送多个请求和接收多个响应。它还提供了对WebSocket协议的支持,可以实现双向通信。 5. 文件上传和下载:OkHttp3提供了方便的API来进行文件上传和下载。您可以使用RequestBody来上传文件,使用ResponseBody来下载文件,并且可以监视进度。 6. 缓存支持:OkHttp3支持HTTP缓存,可以自动处理缓存策略、缓存验证和缓存控制。您可以配置缓存的大小、过期时间等参数。 7. 支持GZIP压缩:OkHttp3支持自动压缩请求和解压缩响应,可以减少数据传输的大小,提高网络性能。 8. 支持HTTPS:OkHttp3支持HTTPS协议,并提供了对TLS和SSL的支持,可以进行安全的通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ByeMoon丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值