美文网首页
OkHttp execute()同步执行流程分析(4.9.3版本

OkHttp execute()同步执行流程分析(4.9.3版本

作者: itzhu | 来源:发表于2021-12-14 10:29 被阅读0次

目的:主要是分析使用execute方法获取response

测试类,建议放到idea里面,对照查看分析。

/**
 * implementation("com.squareup.okhttp3:okhttp:4.9.3")
 * implementation("com.squareup.okhttp3:logging-interceptor:4.9.3")
 * */
object MainApp {

    private val TAG = javaClass.simpleName

    private val okHttpClient = createClient()

    @JvmStatic
    fun test() {
        val request = Request.Builder().get().url("http://www.baidu.com").build()

        try {
            /**1.开始执行请求[RealCall.execute]==>
             * 2.获取一个请求拦截器链,执行各个拦截器,最后返回response[RealCall.getResponseWithInterceptorChain]。
             * */
            val call = okHttpClient.newCall(request)
            //call.timeout().timeout(30,TimeUnit.MILLISECONDS)//设置单个请求的超时时间。具体实现查看[AsyncTimeout]
            val resp = call.execute()
            Log.d(TAG, "resp==" + resp.body?.string())
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

    @JvmStatic
    private fun createClient(): OkHttpClient {
        return OkHttpClient.Builder()

            /**
             * 整个调用过程的超时时间:解析DNS、连接、写入请求体、服务器处理和读取响应体。
             * 如果调用需要重定向或重试,所有这些都必须在一个超时时间内完成。
             * 整个请求开始到结束的时间,如果超时,会调用cancel方法,会抛出InterruptedIOException("timeout")异常。
             * 单个请求设置方法 call.timeout().timeout(30,TimeUnit.MILLISECONDS)
             * 如果是异步请求,则是call的run执行开始计时。
             * */
            .callTimeout(20, TimeUnit.SECONDS)
            //连接超时,socket的连接时间,拦截器里面可以设置
            .connectTimeout(10, TimeUnit.SECONDS)
            //读取超时,socket读取数据超时时间,拦截器里面可以设置
            .readTimeout(10, TimeUnit.SECONDS)
            //写入超时,socket发送数据超时时间,拦截器里面可以设置
            .writeTimeout(10, TimeUnit.SECONDS)
            //拦截器,在拦截器最开始就会被调用
            .addInterceptor(Interceptor { chain ->
                Log.d(TAG, "Interceptor  AA1 ")
                return@Interceptor chain.proceed(chain.request())
            })
            .addInterceptor(Interceptor { chain ->
                Log.d(TAG, "Interceptor  AA2 ")
                return@Interceptor chain.proceed(chain.request())
            })
            //执行请求的拦截器,在请求的http,socket连接之后才会调用。
            .addNetworkInterceptor(Interceptor { chain ->
                Log.d(TAG, "NET Interceptor  BB1 ")
                return@Interceptor chain.proceed(chain.request())
            })
            //打印log
            .addInterceptor(HttpLoggingInterceptor {
                Log.d("HTTPLOG", it)
            }.setLevel(HttpLoggingInterceptor.Level.BODY))
            //请求调度和执行。异步调用的线程池管理,记录所有的请求(同步异步的都记录)。
            // 调用cancelAll,可以取消当前所有请求。
            .dispatcher(Dispatcher())
            .build()
    }

}

执行Call的execute方法,最后是RealCall.getResponseWithInterceptorChain方法获取response,主要看这个方法就好。

execute方法

override fun execute(): Response {
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    //okhttp.builder.callTimeout,从这里开始计时。
    //如果超时时间内,还没有返回response,就会返回timeout。
    //如果一个请求,整个流程要在10s内结束,不管连接、读写时间,
    //反正10s内要结束,就可以设置这个参数。
    timeout.enter()
    
    callStart()
    try {
      client.dispatcher.executed(this)
      return getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }

1.[RealCall.getResponseWithInterceptorChain]

/** 1.[RealCall.getResponseWithInterceptorChain] */
 @Throws(IOException::class)
  internal fun getResponseWithInterceptorChain(): Response {
    // Build a full stack of interceptors.
    val interceptors = mutableListOf<Interceptor>()
    interceptors += client.interceptors//OkHttp创建时添加的拦截器[OkHttpClient.Builder.addInterceptor]
    interceptors += RetryAndFollowUpInterceptor(client)//重试,重定向等
    interceptors += BridgeInterceptor(client.cookieJar)//添加部分请求头数据,必要的请求头
    interceptors += CacheInterceptor(client.cache)//缓存处理
    interceptors += ConnectInterceptor//http连接,主要是socket连接,给出接收和发送数据的操作封装Exchange,供后面的拦截器使用
    if (!forWebSocket) {
      interceptors += client.networkInterceptors//[OkHttpClient.Builder.addNetworkInterceptor]
    }
    interceptors += CallServerInterceptor(forWebSocket)//发送、接收和解析数据,得到服务器Response。
    
    //组装执行链,注意里面的参数和什么时候执行,什么时候创建。
    val chain = RealInterceptorChain(
        call = this,
        interceptors = interceptors,//拦截器
        index = 0,//执行拦截器的位置
        exchange = null,//执行到ConnectInterceptor时,才会创建。
        request = originalRequest,//请求
        connectTimeoutMillis = client.connectTimeoutMillis,//默认为[OkHttpClient.Builder.connectTimeout]
        readTimeoutMillis = client.readTimeoutMillis,//默认为[OkHttpClient.Builder.readTimeout]
        writeTimeoutMillis = client.writeTimeoutMillis//默认为[OkHttpClient.Builder.writeTimeout]
    )

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

2.[RealInterceptorChain.proceed]

/**2.[RealInterceptorChain.proceed]*/
 @Throws(IOException::class)
override fun proceed(request: Request): Response {
  check(index < interceptors.size)

  calls++

  ```

  // Call the next interceptor in the chain.
  val next = copy(index = index + 1, request = request)
  val interceptor = interceptors[index]

  /**
  * 在执行完一个interceptor之后,会使用chain.proceed(chain.request()),
  * 这个chain是第一个RealInterceptorChain copy的(新的chain,只是列表数据是初始的数据),
  * chain的参数每次改变都是新copy一个RealInterceptorChain。
  */
  @Suppress("USELESS_ELVIS")
  val response = interceptor.intercept(next) ?: throw NullPointerException(
      "interceptor $interceptor returned null")
  
  ```

  return response
}

后面只要弄清楚各个Interceptor的功能就行了。

3.[RetryAndFollowUpInterceptor]

主要用于重定向或其他错误处理。
1.具体处理的错误码看[RetryAndFollowUpInterceptor.followUpRequest]方法:

//找出响应接收[userResponse]的HTTP请求。
//这将添加身份验证头、遵循重定向或处理客户端请求超时。
//如果后续操作是不必要的或不适用的,则返回null。
 @Throws(IOException::class)
private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
  val route = exchange?.connection?.route()
  val responseCode = userResponse.code

  val method = userResponse.request.method
  when (responseCode) {
    HTTP_PROXY_AUTH -> {
      val selectedProxy = route!!.proxy
      if (selectedProxy.type() != Proxy.Type.HTTP) {
        throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
      }
      return client.proxyAuthenticator.authenticate(route, userResponse)
    }

    HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)

    HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
      return buildRedirectRequest(userResponse, method)
    }

    HTTP_CLIENT_TIMEOUT -> {
      // 408's are rare in practice, but some servers like HAProxy use this response code. The
      // spec says that we may repeat the request without modifications. Modern browsers also
      // repeat the request (even non-idempotent ones.)
      if (!client.retryOnConnectionFailure) {
        // The application layer has directed us not to retry the request.
        return null
      }

      val requestBody = userResponse.request.body
      if (requestBody != null && requestBody.isOneShot()) {
        return null
      }
      val priorResponse = userResponse.priorResponse
      if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
        // We attempted to retry and got another timeout. Give up.
        return null
      }

      if (retryAfter(userResponse, 0) > 0) {
        return null
      }

      return userResponse.request
    }

    HTTP_UNAVAILABLE -> {
   ...
      return null
    }

    HTTP_MISDIRECTED_REQUEST -> {
      ...
      return userResponse.request
    }

    else -> return null
  }
}

2.重定向请求次数

 /**
   *Chrome遵循21个重定向;Firefox,curl和wget紧随20;Safari是16;HTTP/1.0推荐5。
   */
  private const val MAX_FOLLOW_UPS = 20

4.[BridgeInterceptor]

这个里面主要是添加必要的请求头信息,
比如:"Content-Type","Content-Length","Cookie","User-Agent"等。
还有gzip相关处理。

5.[CacheInterceptor]

一句话,就是缓存处理。
Serves requests from the cache and writes responses to the cache.
服务来自缓存的请求,并将响应写入缓存。

6.[ConnectInterceptor]

会创建socket和加入连接池等,
okHttpClient配置[connectTimeout][readTimeout][writeTimeout]相关参数在这里起作用。
找到或创建一条数据交换通道(socket),返回设置Exchange用于操作request和Response,交给下一个拦截器处理。

@Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    
    /**initExchange
    ==>[ExchangeFinder.find]
    ==>[ExchangeFinder.findHealthyConnection]
    ==>[ExchangeFinder.findConnection]
    ==>[RealConnection.connect(
          connectTimeout,//超时相关参数
          readTimeout,
          writeTimeout,
          pingIntervalMillis,
          connectionRetryEnabled,
          call,
          eventListener
      )]*/
    val exchange = realChain.call.initExchange(chain)
    //设置exchange,交给下一个拦截器处理
    val connectedChain = realChain.copy(exchange = exchange)
    return connectedChain.proceed(realChain.request)
  }

7.client.networkInterceptors

socket连接之后,就是OkHttpClient创建时addNetworkInterceptor里面的拦截器相关的处理了。

8.CallServerInterceptor

This is the last interceptor in the chain. It makes a network call to the server.
这就是最后一个执行的拦截器,注意是最后才开始执行,也是最先执行完的一个拦截器。
使用exchange发送请求相关数据和接收解析返回的数据。
可以理解为里面使用socket,发送HTTP协议相关的数据和接收解析数据。
最后返回response。
这里是得到了后台数据给的response,并不代表最后execute就是返回这个response数据。
比如RetryAndFollowUpInterceptor还要对response做解析处理,log拦截器会打印log等,所有拦截器执行完成,最后得到的response,才算整个请求完成。

总结

整个execute流程分析完成,至于里面的其他细节,不做具体分析了。
知道okhttp创建时,各个参数的含义,拦截器执行的顺序和各自作用,
那么在使用时,就不会有太多迷茫的位置了。

相关文章

网友评论

      本文标题:OkHttp execute()同步执行流程分析(4.9.3版本

      本文链接:https://www.haomeiwen.com/subject/qpyefrtx.html