28

一文彻底搞懂OkHttp

 4 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzIzNTc5NDY4Nw%3D%3D&%3Bmid=2247485055&%3Bidx=1&%3Bsn=cb16f027149593b940685dc47ee3a30a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一直都想写一篇有关 OkHttp 的源码文章,今天推荐一篇关于解析 OkHttp 源码的文章,希望大家能有所收获。

作者:RayC

链接:https://juejin.cn/post/6909445385266135048

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<Interceptor> = mutableListOf()//整体流程拦截器
    internal val networkInterceptors: MutableList<Interceptor> = 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<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
    internal var protocols: List<Protocol> = 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
  }
}

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<Interceptor>()
  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<AsyncCall>()
  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方法,执行内部逻辑,具体逻辑和同步请求的逻辑基本一致,这里就不再赘述。请求完成后,不管结果成功失败,都会调用Dispatcher的finished方法。

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

private fun <T> finished(calls: Deque<T>, 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)
}

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

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,这是一个网络请求的编码器,针对不同的协议会采用不同的方式来进行编码传输。

zquuqqa.jpg!mobile
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
  }
}

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

  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后,一次网络请求也算完成啦!

总结

话不多说,先上图

JJ7rMfy.jpg!mobile

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

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

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

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

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

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

QrQfuuq.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK