网络框架-OkHttp源码解析

不开心,就算长生不老也没用,开心,就算只能活几天也足够!——《大话西游之月光宝盒》

前言

本文从源码角度分析OkHttp的工作流程,源码基于4.4.0。

OkHttp基本实现原理

OkHttp的内部实现通过一个责任链模式完成,将网络请求的各个阶段封装到各个链条中,实现了各层的解耦。

OkHttp使用流程

第一步:创建OkHttpClient

OkHttpClient是okhttp3框架的客户端,用于发送http请求(Requests)和读取读取网络返回数据(Responses)。因为OkHttpClient拥有自己的连接池和线程池,所以官方建议使用单例创建OkHttpClient,即一个进程中只创建一次即可,以后的每次网络请求都使用该实例。这样做利于减少延迟和节省内存。
创建OkHttpClient的两种方式:

//方式一:使用默认网络配置创建OkHttpClient实例
OkHttpClient client = new OkHttpClient();

//方式二:通过构建者模式自定义网络配置创建OkHttpClient实例
 OkHttpClient client = new OkHttpClient.Builder()
                ...//通过Builder提供的方法设置网络参数
                .build();

OkHttpClient.Builder类的部分变量如下所示,通过其提供的方法可以设置部分参数的值

   //调度器,实现同步 /异步的关键,后文会进行讲解
    internal var dispatcher: Dispatcher = Dispatcher()
    //连接池
    internal var connectionPool: ConnectionPool = ConnectionPool()
    //各种拦截器
    internal val interceptors: MutableList = mutableListOf()
    internal val networkInterceptors: MutableList = mutableListOf()
    //连接失败后自动重试,默认true
    internal var retryOnConnectionFailure = true
    internal var authenticator: Authenticator = Authenticator.NONE
    internal var followRedirects = true
    internal var followSslRedirects = true
    //网络请求Cookie
    internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
    //网络请求的缓存
    internal var cache: Cache? = null
    internal var dns: Dns = Dns.SYSTEM
    internal var proxy: Proxy? = null
    internal var proxySelector: ProxySelector? = null
    internal var proxyAuthenticator: Authenticator = Authenticator.NONE
    internal var socketFactory: SocketFactory = SocketFactory.getDefault()
    internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
    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
    //网络连接超时时间
    internal var connectTimeout = 10_000
    //数据读取超时时间
    internal var readTimeout = 10_000
    //数据写入超时时间
    internal var writeTimeout = 10_000
    internal var pingInterval = 0
    internal var routeDatabase: RouteDatabase? = null

第二步:创建网络请求描述对象Request

Request对象通过参数设置用于描述一次网络请求。它的实例创建同样采用了构建者模式,创建Request实例的代码如下:

Request request = new Request.Builder().
                url("换成要请求的url地址").
                ...//通过Builder提供的方法设置网络参数
                build();

Request.Builder()的部分源码如下:

public open class Builder {
        public constructor() { /* compiled code */ }

        internal constructor(request: okhttp3.Request) { /* compiled code */ }

        internal final var body: okhttp3.RequestBody? /* compiled code */

        internal final var headers: okhttp3.Headers.Builder /* compiled code */

        internal final var method: kotlin.String /* compiled code */

        internal final var url: okhttp3.HttpUrl? /* compiled code */

       ...//省略了部分对外提供的参数设置方法
    }

可见通过Request.Builder()可以设置如下常用的网络参数:
headers:请求头;
body:请求体;
method:请求方式,比如:get/post/put...等请求方式;
url:请求地址;

第三步:创建网络请求对象Call

Call为一个接口,其实现类为RealCall。一个Call对象封装表示了一次网络请求,通过它能够执行或者取消一次网络请求,并且每一次网络请求都会生产一个新的Call对象,同一个Call对象不能被执行两次。Call对象的创建需要用到前面创建好的OkHttpClient对象的newCall(Request request)方法,并需要将第二步创建好的用于描述此次网络请求的Request对象作为参数传进去,整体代码如下:

OkHttpClient client = new OkHttpClient.Builder()
                ...//通过Builder提供的方法设置网络参数
                .build();
Request request = new Request.Builder().
                url("换成要请求的url地址").
                ...//通过Builder提供的方法设置网络参数
                build();
Call call = client.newCall(request);

我们来看OkHttpClient的newCall(Request request)方法,其源码如下:

override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

可见最后创建的是RealCall对象。

第四步:通过Call对象执行网络请求

okhttp3中提供了两种网络请求方式:一种是同步请求,调用call.execute()方法;另一种是异步请求,调用call.enqueue(Callback callback)方法。在分析两种请求方式之前,先来看看OkHttp中一个重要成员Dispatcher(调度器)。

Dispatcher(调度器)

Dispatcher是OkHttp的任务调度的核心类,负责管理同步和异步的请求,并管理每一个请求任务的请求状态,其内部维护了一个线程池用于执行相应的请求。Dispatcher定义默认的最大并发请求量 maxRequests = 64 和单个host支持的最大并发量 maxRequestsPerHost = 5。并且在其内部维护了三个双端阻塞队列用于管理网络任务,代码如下:

class Dispatcher constructor() {
...
//默认的最大并发请求量为64
@get:Synchronized var maxRequests = 64
...
//默认单个host支持的最大并发量为5
@get:Synchronized var maxRequestsPerHost = 5
...
  /** 存储准备好的异步调用任务,它们将顺序执行 */
  private val readyAsyncCalls = ArrayDeque()

  /**存储异步运行中的网络任务,包括已经被取消但还未结束的网络任务*/
  private val runningAsyncCalls = ArrayDeque()

  /**存储同步运行中的网络任务,包括已经被取消但还未结束的网络任务*/
  private val runningSyncCalls = ArrayDeque()
...
}

关于这3个双端阻塞队列的作用,可以这么理解:把Dispatcher当成生产者,把线程池当成消费者,当生产者生产的网络请求数量大于消费者所能承受的最大范围,就把未能及时执行的任务保存在readyAsyncCalls队列中,当时机成熟,也就是线程池有空余线程可以执行时,会调用promoteCall()这个方法把等待队列中的任务取出放到线程池中执行,并且把这个任务转移到runningAsyncCalls或者runningSyncCalls 队列中去。

为什么要使用双端队列?很简单因为网络请求执行顺序跟排队一样,讲究先来后到,新来的请求放队尾,执行请求从对头部取。我们知道LinkedList同样也实现了Deque接口,内部是用链表实现的双端队列,那为什么不用LinkedList呢?实际上这与readyAsyncCalls向runningAsyncCalls转换有关,当执行完一个请求或调用enqueue方法入队新的请求时,会对readyAsyncCalls进行一次遍历,将那些符合条件的等待请求转移到runningAsyncCalls队列中并交给线程池执行。尽管二者都能完成这项任务,但是由于链表的数据结构致使元素离散的分布在内存的各个位置,CPU缓存无法带来太多的便利,另外在垃圾回收时,使用数组结构的效率要优于链表。

接下来咱们结合上面说的Dispatcher分别看看同步请求和异步请求的实现过程,并详细说一下他们是如何实现的。
请求方式一:同步请求
call.execute()源码如下:

override fun execute(): Response {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    timeout.enter()
    callStart()
    try {
      //①调用调度器dispatcher的executed(Call call)方法
      client.dispatcher.executed(this)
      return getResponseWithInterceptorChain()
    } finally {
      //②调用分配器Dispatcher的finish()方法
      client.dispatcher.finished(this)
    }
  }

注释①部分调用了 client.dispatcher的executed()方法,再来看client.dispatcher的executed(call: RealCall)方法:

@Synchronized internal fun executed(call: RealCall) {
    runningSyncCalls.add(call)
  }

由此可见,注释①部分是将这次网络请求RealCall实例存进了runningSyncCalls这双端阻塞队列;
接着方法又调用了getResponseWithInterceptorChain()方法并返回。关于getResponseWithInterceptorChain()先放一边,在异步请求中再一起讲解。

接下来看注释②,不管结果交易结果如何,都会调用finally中的client.dispatcher.finished(this)将本次请求从队列中移除。
请求方式二:异步请求
call.enqueue(Callback callback)源码如下:

override fun enqueue(responseCallback: Callback) {
    synchronized(this) {
      check(!executed) { "Already Executed" }
      executed = true
    }
    callStart()
    //①调用调度器dispatcher的enqueue(AsyncCall call)方法
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }

可见其内部调用了client.dispatcher.enqueue(AsyncCall call)方法,分析该方法之前先来看看其参数AsyncCall类的定义:

internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
    @Volatile var callsPerHost = AtomicInteger(0)
      private set

    fun reuseCallsPerHostFrom(other: AsyncCall) {
      this.callsPerHost = other.callsPerHost
    }

    val host: String
      get() = originalRequest.url.host

    val request: Request
        get() = originalRequest

    val call: RealCall
        get() = this@RealCall

    /**
     * Attempt to enqueue this async call on [executorService]. This will attempt to clean up
     * if the executor has been shut down by reporting the call as failed.
     */
    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!
        }
      }
    }

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

跟踪源码发现AsyncCall是RealCall的一个内部类,其实现了Runnable接口,所以AsyncCall就是一个Runnable的实现,当在线程池中执行该对象时就会执行其实现的run()方法,通过注释①处源码了解到和同步请求方法一样它也会去调用getResponseWithInterceptorChain()方法。

现在回到client.dispatcher.enqueue(AsyncCall call)方法,其源码如下:

internal fun enqueue(call: AsyncCall) {
    synchronized(this) {
      readyAsyncCalls.add(call)

      // ①尝试判断并复用同一host请求的实例
      if (!call.call.forWebSocket) {
        val existingCall = findExistingCallWithHost(call.host)
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
      }
    }
    promoteAndExecute()
  }

可见方法会先将请求放入readyAsyncCalls双端阻塞队列,然后会在注释①出尝试判断并复用同一host请求的实例,findExistingCallWithHost(String 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
  }

其作用为循环遍历readyAsyncCalls和runningAsyncCalls列表寻找host值相同的call实例。如果找到Call实例就会调用call.reuseCallsPerHostFrom(AsyncCall other)方法,其源码如下:

internal inner class AsyncCall(
    private val responseCallback: Callback
  ) : Runnable {
@Volatile var callsPerHost = AtomicInteger(0)
      private set

fun reuseCallsPerHostFrom(other: AsyncCall) {
      this.callsPerHost = other.callsPerHost
    }
}

其作用为给AsyncCall实例的成员变量callsPerHost赋值,即共享同一host下AsyncCall请求实例的AtomicInteger值。

接着client.dispatcher.enqueue( AsyncCall call)最终会调用promoteAndExecute()方法,其源码如下:

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

    val executableCalls = mutableListOf()
    val isRunning: Boolean
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      //遍历readyAsyncCalls
      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()
        //per host 计数加1
        asyncCall.callsPerHost.incrementAndGet()
        executableCalls.add(asyncCall)
        //移入runningAsyncCalls列表
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }

    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      //①提交任务到线程池
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }

可见其主要逻辑为遍历readyAsyncCalls队列,其中满足阈值校验的AsyncCall对象被移至runningAsyncCalls队列同时提交到线程池执行。注释①中可见提交到线程池使用到了executorService对象,现在来看看executorService的定义:

@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!!
    }

这不是一个newCachedThreadPool吗?没错,除了最后一个threadFactory参数之外与newCachedThreadPool的源码实现一模一样,只不过是多设置了线程名字方便排查问题。其阻塞队列采用的SynchronousQueue,它的特点是不存储数据,当添加一个元素时,必须等待一个消费线程取出它,否则一直阻塞,如果当前有空闲线程则直接在这个空闲线程执行,如果没有则新启动一个线程执行任务。通常用于需要快速响应任务的场景,在网络请求要求低延迟的大背景下比较合适。可见这个executorService对象就是Dispatcher自身维护的用于执行网络任务的线程池。
接着来看注释①中执行的AsyncCall.executeOn(ExecutorService executorService)方法的源码:

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

可见其功能为将AsyncCall对象加入到executorService线程池并执行,并在线程结束或者执行失败后清除AsyncCall请求对象。通过上面对AsyncCall类的分析可知,AsyncCall类是一个Runnable的实现,将asyncCall对象加入线程池执行会去执行其内部实现的run()方法并最终调getResponseWithInterceptorChain()

法来获取网络请求的返回值。AsyncCall内部实现的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)
        }
      }
    }

在分析获取网络请求返回值getResponseWithInterceptorChain()方法之前再插一句,通过上面的分析,不管同步请求还是异步请求,在请求正常或者异常结束后都会调用了client.dispatcher.finished(call: RealCall)或client.dispatcher.finished(call: AsyncCall)方法,那我们就来看看其源码做了什么:

  //异步任务执行结束
  internal fun finished(call: AsyncCall) {
    call.callsPerHost.decrementAndGet()
    finished(runningAsyncCalls, call)
  }

  //同步任务执行结束
  internal fun finished(call: RealCall) {
    finished(runningSyncCalls, 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
    }
    //这个方法在前面已经分析过了,用于将readyAsyncCalls队列中的请求移入runningAsyncCalls队列中,并交由线程池执行。
    val isRunning = promoteAndExecute()
    //如果没有请求需要执行,回调闲置callback
    if (!isRunning && idleCallback != null) {
      idleCallback.run()
    }
  }

方法的主要逻辑已在注释中写出,需要注意的是promoteAndExecute()方法在enqueue和finish方法中都会调用,即当有新的请求入队和当前请求完成后,需要重新提交一遍任务到线程池。
到目前为止我们通过源码分析了解到,其实OkHttp的同步请求call.execute()和异步请求call.enqueue(Callback callback)其实都是通过OkHttpClient中维护的Dispatcher调度器对象来实现的,而异步任务又是队列的方式在Dispatcher调度器对象自身维护的ExecutorService线程池对象中按顺序执行的。不同网络任务的执行状态又分别保存在Dispatcher调度器对象自身维护的3个不同的双端阻塞队列中。

下面终于到了OkHttp原理的重点和难点部分了,OkHttp的内部实现通过一个责任链模式完成,将网络请求的各个阶段封装到各个链条中,实现了各层的解耦。而这个责任链模式的具体实现就在getResponseWithInterceptorChain()方法中。

getResponseWithInterceptorChain方法(OKHTTP责任链模式的实现)

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
    }
    //添加请求拦截器
    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)
      }
    }
  }

这里我们先不关心每个拦截器具体作用是什么,主流程最终走到chain.proceed(originalRequest)。我们先来看一下这个procceed方法:

@Throws(IOException::class)
  override fun proceed(request: Request): Response {
    check(index < interceptors.size)
    // 统计当前拦截器调用proceed方法的次数
    calls++

    if (exchange != null) {
      // exchage是对请求流的封装,在执行ConnectInterceptor前为空,连接和流已经建立但此时此连接不再支持当前url说明之前的网络拦截器对url或端口进行了修改,这是不允许的!!
      check(exchange.connection.supportsUrl(request.url)) {
        "network interceptor ${interceptors[index - 1]} must retain the same host and port"
      }
      // 这里是对拦截器调用proceed方法的限制,在ConnectInterceptor及其之后的拦截器最多只能调用一次proceed!!
      check(calls == 1) {
        "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
      }
    }

    // 创建下一层责任链 注意index + 1
    val next = copy(index = index + 1, request = request)
    //取出下标为index的拦截器,并调用其intercept方法,将新建的链传入。
    val interceptor = interceptors[index]
    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    if (exchange != null) {
      // 保证在ConnectInterceptor及其之后的拦截器至少调用一次proceed!!
      check(index + 1 >= interceptors.size || next.calls == 1) {
        "network interceptor $interceptor must call proceed() exactly once"
      }
    }
    //保证未返回含有空body值的response
    check(response.body != null) { "interceptor $interceptor returned a response with no body" }

    return response
  }

代码中的注释已经写得比较清楚了,总结起来就是创建下一级责任链,然后取出当前拦截器,调用其intercept方法并传入创建的责任链。++为保证责任链能依次进行下去,必须保证除最后一个拦截器(CallServerInterceptor)外,其他所有拦截器intercept方法内部必须调用一次chain.proceed()方法++,如此一来整个责任链就运行起来了。
比如ConnectInterceptor源码中:

object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    //创建连接和流   
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange = exchange)
    //执行下一级责任链
    return connectedChain.proceed(realChain.request)
  }
}

除此之外在责任链不同节点对于proceed的调用次数有不同的限制,ConnectInterceptor拦截器及其之后的拦截器能且只能调用一次,因为网络握手、连接、发送请求的工作发生在这些拦截器内,表示正式发出了一次网络请求;而在这之前的拦截器可以执行多次proceed,比如错误重试。

经过责任链一级一级的递推下去,最终会执行到CallServerInterceptor的intercept方法,此方法会将网络响应的结果封装成一个Response对象并return。之后沿着责任链一级一级的回溯,最终就回到getResponseWithInterceptorChain方法的返回。

拦截器分类

大致总结责任链的各个节点拦截器的作用:

拦截器 作用
应用拦截器 拿到的是原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等。
RetryAndFollowUpInterceptor 处理错误重试和重定向
BridgeInterceptor 应用层和网络层的桥接拦截器,主要工作是为请求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要进行解压。
CacheInterceptor 缓存拦截器,如果命中缓存则不会发起网络请求。
ConnectInterceptor 连接拦截器,内部会维护一个连接池,负责连接复用、创建连接(三次握手等等)、释放连接以及创建连接上的socket流。
networkInterceptors(网络拦截器) 用户自定义拦截器,通常用于监控网络层的数据传输。
CallServerInterceptor 请求拦截器,在前置准备工作完成后,真正发起了网络请求。

参考文章
https://blog.csdn.net/qq_29152241/article/details/82011539
https://www.jianshu.com/p/8bcfb10243a1

你可能感兴趣的:(网络框架-OkHttp源码解析)