网络请求框架OkHttp4的使用与原理解析01:任务调度与拦截器分析

OkHttp任务调度流程:

image

1. OkHttpClient构建过程分析

案例:

OkHttpClient client = new OkHttpClient.Builder()
      .addInterceptor(new CustomInterceptor())
      .cache(new Cache(cacheDir, cacheSize))
      .readTimeout(1000, TimeUnit.MILLISECONDS)
      .writeTimeout(1000, TimeUnit.MILLISECONDS)
      .build();

 OkHttp使用建造者模式来构建一个OkHttpClient(客户端)对象,完成以下配置:

  • a. 指定自定义拦截器Interceptor;
  • b. 指定Cache缓存目录;
  • c. 指定DNS域名解析服务器、代理服务器Proxy;
  • d. 指定超时时间(请求超时、连接超时、读超时、写超时);
  • e. 创建一个任务调度器Dispatcher;
  • f. 创建一个连接池ConnectionPool(复用Socket连接)等等;
open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {

    /***
       * 1. 创建网络请求客户端OkHttpClient,分为两步
       * (1) 创建构建者OkHttpClient.Builder,并配置构建参数;
       * (2) 由构建者构建客户端OkHttpClient
       * var client = OkHttpClient.newBuilder()
       *              .addInterceptor(...)
       *              .dns(...)
       *              .proxy(...)
       *              .build()
       */

      //(1)创建构建客户端的构建者对象
      // 即var builder = OkHttpClient.newBuilder()
      // 在Builder构建者的构造函数中,初始化用于构建OkHttpClient对象的各类参数
      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            // 请求失败时是否重试,默认为true
        internal var authenticator: Authenticator = Authenticator.NONE
        internal var followRedirects = true
        internal var followSslRedirects = true
        internal var cookieJar: CookieJar = CookieJar.NO_COOKIES // 持久连接Cookie
        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<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          // 请求超时时间
        internal var connectTimeout = 10_000  // 连接超时时间
        internal var readTimeout = 10_000     // 写操作超时时间
        internal var writeTimeout = 10_000    // 读操作超时时间
        internal var pingInterval = 0
        internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
        internal var routeDatabase: RouteDatabase? = null

        //(2)使用构建者OkHttpClient.Builder构建客户端OkHttpClient对象
        // 在OkHttpClient构造方法中,会依次将构建者配置的参数
        // 赋值给OkHttpClient
        fun build(): OkHttpClient = OkHttpClient(this)
}

2. Request构建过程分析

示例代码:

// GET请求
Request request = new Request.Builder()
     .url("https://github.com/")
     .header("User-Agent", "OkHttp Headers.java")
     .addHeader("Accept", "application/json; q=0.5")
     .addHeader("Accept", "application/vnd.github.v3+json")
     .build();

// POST请求(发送String)
public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");
String postBody = ""
    + "Releases\n"
    + "--------\n"
    + "\n"
    + " * _1.0_ May 6, 2013\n"
    + " * _1.1_ June 15, 2013\n"
    + " * _1.2_ August 11, 2013\n";

Request request = new Request.Builder()
    .url("https://api.github.com/markdown/raw")
    .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
    .build();

// POST请求(发送Stream)
RequestBody requestBody = new RequestBody() {
  @Override public MediaType contentType() {
    return MEDIA_TYPE_MARKDOWN;
  }

  @Override public void writeTo(BufferedSink sink) throws IOException {
    sink.writeUtf8("Numbers\n");
    sink.writeUtf8("-------\n");
    for (int i = 2; i <= 997; i++) {
      sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
    }
  }

  private String factor(int n) {
    for (int i = 2; i < n; i++) {
      int x = n / i;
      if (x * i == n) return factor(x) + " × " + i;
    }
    return Integer.toString(n);
  }
};

Request request = new Request.Builder()
    .url("https://api.github.com/markdown/raw")
    .post(requestBody)
    .build();

// POST请求(发送文件)
File file = new File("README.md");

Request request = new Request.Builder()
    .url("https://api.github.com/markdown/raw")
    .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
    .build();

// POST请求(发送Form表单-键值对)
RequestBody formBody = new FormBody.Builder()
    .add("search", "Jurassic Park")
    .build();
Request request = new Request.Builder()
    .url("https://en.wikipedia.org/w/index.php")
    .post(formBody)
    .build();

// POST请求(multipart数据)
RequestBody requestBody = new MultipartBody.Builder()
    .setType(MultipartBody.FORM)
    .addFormDataPart("title", "Square Logo")
    .addFormDataPart("image", "logo-square.png", RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
    .build();

Request request = new Request.Builder()
    .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
    .url("https://api.imgur.com/3/image")
    .post(requestBody)
    .build();

 OkHttp使用建造者模式来构建一个Request HTTP请求,完成以下配置:

  • a. 指定HTTP请求HttpUrl;
  • b. 指定HTTP请求Headers(注:也是使用建造者模式构建Headers.Builder);
  • c. 指定HTTP请求方法method,默认为GET方法;
  • d. 指定HTTP请求实体RequestBody,针对于POST方法。
class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map<Class<*>, Any>
) {

    /**
       *  构建网络请求对象Requst,分为两步:
       *  (1)创建构建Request对象的构建者Request.Builder;
       *  (2)使用构建者构建Request对象
       *  var request = Request.Builder()
       *                .url(...)
       *                .header(...)
       *                .build()
       */

      // (1)创建构建Request对象的构建者Request.Builder
      // var requestBuilder = Request.newBuilder()
      open class Builder {
        internal var url: HttpUrl? = null      // host域名
        internal var method: String            // 请求方法,默认为"GET"
        internal var headers: Headers.Builder  // 请求头Headers
        internal var body: RequestBody? = null // 请求实体RequestBody

        internal var tags: MutableMap<Class<*>, Any> = mutableMapOf()
                                                // tag集合
        constructor() {
          this.method = "GET"
          this.headers = Headers.Builder()
        }

        // 指定HTTP头部
        open fun header(name: String, value: String) = apply {
          headers[name] = value
        }

        open fun addHeader(name: String, value: String) = apply {
          headers.add(name, value)
        }

        // 指定HTTP请求方法
        open fun get() = method("GET", null)

        open fun head() = method("HEAD", null)

        open fun post(body: RequestBody) = method("POST", body)

        @JvmOverloads
        open fun delete(body: RequestBody? = EMPTY_REQUEST) = method("DELETE", body)

        open fun put(body: RequestBody) = method("PUT", body)

        open fun patch(body: RequestBody) = method("PATCH", body)

        // (2) 使用构建者构建Request对象
        open fun build(): Request {
          return Request(
              checkNotNull(url) { "url == null" },
              method,
              headers.build(),
              body,
              tags.toImmutableMap()
          )
        }
}

3. 网络通信过程分析

案例

// 同步请求
Response response = client.newCall(request).execute();

// 异步请求
client.newCall(request).enqueue(new Callback() {
  @Override public void onFailure(Call call, IOException e) {
    e.printStackTrace();
  }
  
  @Override public void onResponse(Call call, Response response) throws IOException {
    ResponseBody responseBody = response.body());
    ...
  }
});

 任务调度器、责任链模式(拦截器链)。

(1) 获取网络请求器Call对象;

/**
* 1. 创建发起网络请求的Call对象,通过它可以执行同步请求和异步请求
* 调用客户端OkHttpClient.newCall(request)方法实现
* Call是一个接口,它的实现类为RealCall,它是衔接应用层与网络层的桥梁
*/
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)

(2) 同步请求

class RealCall(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {

    // 3-1 发起同步网络请求
  override fun execute(): Response {
    // (1) 检查当前Call是否被执行
    // 如果正在执行,抛出"IllegalStateException"异常
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    timeout.enter()
    callStart()
    try {
      // (2) 调用任务调度器的executed方法
      // 将Call任务添加到同步执行队列中即runningSyncCalls(ArrayDeque<RealCall>())
      // 即runningSyncCalls.add(call)
      client.dispatcher.executed(this)
      //(3)通过拦截器链获得响应Response
      // 并直接返回(注意:执行完getResponseWithInterceptorChain()后,会接着执行finally语句,最后再执行return)
      return getResponseWithInterceptorChain()
    } finally {
      //(4)网络请求完毕或者异常
      // 从runningSyncCalls队列中移除当前任务
      client.dispatcher.finished(this)
    }
  }
}


class Dispatcher constructor() {

    ...
    
    // 同步执行队列
    // ArrayDeque即可作为Stack,也可作为Queue,它是线程非安全的
   private val runningSyncCalls = ArrayDeque<RealCall>()

  @Synchronized internal fun executed(call: RealCall) {
    // 将Call任务添加到同步执行队列中ArrayDeque
    runningSyncCalls.add(call)
  }
  
  private fun <T> finished(calls: Deque<T>, call: T) {
      val idleCallback: Runnable?
      synchronized(this) {
        // a. 从对应队列中移除任务RealCall(同步任务)、AsyncCall(异步任务,Runnable)
        if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
        idleCallback = this.idleCallback
      }
  
      //  b. 对于异步请求来说,遍历等待队列
      //  从等待队列中取新的任务,插入到执行队列中
      val isRunning = promoteAndExecute()
  
      if (!isRunning && idleCallback != null) {
        idleCallback.run()
      }
    }
 
}

(3) 异步请求

class RealCall(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */
  val originalRequest: Request,
  val forWebSocket: Boolean
) : Call {

  // 3-2 发起异步网络请求
  override fun enqueue(responseCallback: Callback) {
    // (1) 检查当前Call是否被执行
    // 如果正在执行,抛出"IllegalStateException"异常
    check(executed.compareAndSet(false, true)) { "Already Executed" }

    callStart()
    //(2) 调用任务调度器的enqueue方法
    // 注:AsyncCall是RealCall的内部类,继承于Runnable
    client.dispatcher.enqueue(AsyncCall(responseCallback))
  }
}
  • Dispatcher:执行任务调度 (执行队列、等待队列)
class Dispatcher constructor() {

    // 异步等待队列
    private val readyAsyncCalls = ArrayDeque<AsyncCall>()
    
    /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
    // 异步执行队列
    private val runningAsyncCalls = ArrayDeque<AsyncCall>()
    
    /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
    // 同步执行队列
    // ArrayDeque即可作为Stack,也可作为Queue,它是线程非安全的
    private val runningSyncCalls = ArrayDeque<RealCall>()
    
    constructor(executorService: ExecutorService) : this() {
        this.executorServiceOrNull = executorService
    }
    
    internal fun enqueue(call: AsyncCall) {
        synchronized(this) {
          // 3-2-(2)-a 首先,将异步任务AsyncCall添加到等待队列中
          readyAsyncCalls.add(call)
          if (!call.call.forWebSocket) {
            val existingCall = findExistingCallWithHost(call.host)
            if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
          }
        }
        // 3-2-(2)-a 然后,从等待队列取任务插入到执行队列中
        // 再在线程池中执行新任务
        promoteAndExecute()
    }
    
  private fun promoteAndExecute(): Boolean {
    this.assertThreadDoesntHoldLock()

    val executableCalls = mutableListOf<AsyncCall>()
    val isRunning: Boolean
    // b-1 遍历等待队列,决定是否将其添加到可执行队列中(线程安全)
    // - 如果执行队列中正在执行的任务数量>=maxRequests,拒绝添加任何新任务到执行队列;
    // - 如果当前Host的请求数超过maxRequestsPerHost,拒绝本次新任务到执行队列;
    // - 否则,将等待队列中的任务添加到执行队列,并从等待队列删除任务
    synchronized(this) {
      val i = readyAsyncCalls.iterator()
      while (i.hasNext()) {
        val asyncCall = i.next()
        // 最大并发量判断,maxRequests=64
        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        // Host允许最大请求数判断,maxRequestsPerHost = 5
        if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
        // 从等待队列移除
        i.remove()
        asyncCall.callsPerHost.incrementAndGet()
        // 将新任务添加到executableCalls列表中
        executableCalls.add(asyncCall)
        // 将其添加到执行队列中
        runningAsyncCalls.add(asyncCall)
      }
      isRunning = runningCallsCount() > 0
    }
    // b-2 遍历executableCalls列表
    // 依次将要执行的新任务放在线程池中执行,调用asyncCall.executeOn实现
    // (1)将新任务插入到线程池执行, executorService.execute(this);
    // (2)执行异常或执行完毕均尝试从等待队列中取新任务任务, client.dispatcher.finished(this);
    // 注:具体的任务执行流程,请看AsyncCall.run()方法
    for (i in 0 until executableCalls.size) {
      val asyncCall = executableCalls[i]
      
      asyncCall.executeOn(executorService)
    }

    return isRunning
  }
}
  • AsyncCall:继承于Runnable,被添加到线程池中执行,子线程中执行拦截器链逻辑,即getResponseWithInterceptorChain
inner class AsyncCall(
    private val responseCallback: Callback
) : Runnable {
    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方法
        responseCallback.onFailure(this@RealCall, ioException)
      } finally {
        // 如果本次任务执行异常,从等待队列中取下一个任务
        if (!success) {
          client.dispatcher.finished(this) // This call is no longer running!
        }
      }
    }

    // 3-2 异步任务执行流程
    override fun run() {
      threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = false
        timeout.enter()
        try {
          // (1)调用拦截器链得到响应数据reponse
          val response = getResponseWithInterceptorChain()
          signalledCallback = true
          // (2)请求成功,将异步请求响应结果回调出去
          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 {
            //(3)请求失败,将异常信息回调出去
            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 {
          // (4)从等待队列中取下一批任务添加到线程池中执行
          // 并更新等待队列和执行队列的信息
          client.dispatcher.finished(this)
        }
      }
    }
}

(4) 拦截器链功能

 OkHttp框架使用责任链模式一步一步完成整个网络通信,每个拦截器有自己的功能。

  • 自定义拦截器;
  • RetryAndFollowUpInterceptor:重定向拦截器,用于处理错误和进行重定向;
  • BridgeInterceptor:桥梁拦截器,用于构建网络连接桥梁。将用户请求转换为网络请求,将网络响应转换为用户响应;
  • CacheInterceptor:缓存拦截器,用于从缓存中获取服务器请求,或者把服务器响应写入缓存中;
  • ConnectInterceptor:连接拦截器,用于打开一个Socket连接,去连接目标服务器;
  • CallServerInterceptor:请求服务器拦截器,拦截器链的最后一个拦截器,执行通过网络请求服务器,即完成Socket通信。

具体功能:

(1)RetryAndFollowUpInterceptor:重定向拦截器,用于处理错误和进行重定向;

  • a. 获取下一个要执行的拦截器BridgeInterceptor;
  • b. 开始轮询,通过BridgeInterceptor返回的用户响应respone,是否需要重定向。如果response的followUp==null,说明请求成果,返回用户响应response;否则,执行重定向请求,需要注意的时当重定向的次数超过20次抛出异常;

源码如下:

/**
   * 4-1 RetryAndFollowUpInterceptor拦截器
   * 用于处理错误和进行重定向
    */
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    //(1)获取下一个要执行的拦截器
    // 注:每一个拦截器存储的request和call信息是一致的
    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>()
    // (2)开始轮询,即是否需要重定向,当次数超过20次抛出异常
    while (true) {
      call.enterNetworkInterceptorExchange(request, newExchangeFinder)

      var response: Response
      var closeActiveExchange = true
      try {
        // a. 如果请求被取消,抛出IOException
        if (call.isCanceled()) {
          throw IOException("Canceled")
        }
        // b. 调用下一个拦截器BridgeInterceptor
        // 将用户请求request作为参数传入
        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
        }
        //--------------------------------------------------------------------
        //--------------------------------------------------------------------
        // //  处理Bridge拦截器返回的用户响应  
        //--------------------------------------------------------------------
        //--------------------------------------------------------------------
        // Attach the prior response if it exists. Such responses never have a body.
        // c. 检测前一个Response
        if (priorResponse != null) {
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
              .build()
        }

        val exchange = call.interceptorScopedExchange
        val followUp = followUpRequest(response, exchange)
        // d. followUp为null,表示不要重定向,释放资源并且返回response
        if (followUp == null) {
          if (exchange != null && exchange.isDuplex) {
            call.timeoutEarlyExit()
          }
          closeActiveExchange = false
          return response
        }

        val followUpBody = followUp.body
        if (followUpBody != null && followUpBody.isOneShot()) {
          closeActiveExchange = false
          return response
        }

        // e. 否则,关闭response的body
        // 并重定向,注意,最大重定向次数不能超过20次
        response.body?.closeQuietly()

        if (++followUpCount > MAX_FOLLOW_UPS) {
          throw ProtocolException("Too many follow-up requests: $followUpCount")
        }

        request = followUp
        priorResponse = response
      } finally {
        call.exitNetworkInterceptorExchange(closeActiveExchange)
      }
    }
  }

(2)BridgeInterceptor:桥梁拦截器,用于构建网络连接桥梁。将用户请求转换为网络请求,将网络响应转换为用户响应;

  • a. 将用户请求userRequest将转为服务器请求request,使用建造者模式实现;
  • b. 调用下一个拦截器CacheInterceptor,并将服务器请求作为参数传入;
  • c. 将网络响应response转换为用户响应,其中response由CahceInterceptor返回。

源码如下:

/**
   * 4-2 BridgeInterceptor拦截器
   * 构建网络连接桥梁。将用户请求转换为网络请求,将网络响应转换为用户响应
   */
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    // (1)将用户请求userRequest将转为网络请求
    // "Connection": "Keep-Alive"
    // "Transfer-Encoding": "chunked"
    // "Accept-Encoding": "gzip"
    // ...
    val userRequest = chain.request()
    val requestBuilder = userRequest.newBuilder()

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

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    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)
    }
    // (2) 调用下一个拦截器
    // 并将服务器请求作为参数传入CacheInterceptor
    val networkResponse = chain.proceed(requestBuilder.build())

    cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)

    //--------------------------------------------------------------------
    //--------------------------------------------------------------------
    // //  处理Cache拦截器返回的网络响应  
    //--------------------------------------------------------------------
    //--------------------------------------------------------------------

    // (3) 将网络响应转换为用户响应
    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()
  }

(3)CacheInterceptor:缓存拦截器,用于从缓存中获取服务器请求,或者把服务器响应写入缓存中;

  • a. 根据请求获“候选”缓存,如果Cache中有的话;
  • b. 获取缓存策略strategy,通过工厂模式实现;
  • c. 检测缓存是否有效,如果网络被禁止直接返回缓存Response;
  • d. 调用下一个拦截器,即ConnectInteceptor,从网络中获取响应response;
  • e. 将网络响应返回给上一个拦截器,并更新缓存。
/**
   * 4-3 CacheInterceptor拦截器
   * 从缓存中获取服务器请求,或者把服务器响应写入缓存中
   */
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val call = chain.call()
    // (1) 根据请求获“候选”缓存,如果Cache中有的话
    val cacheCandidate = cache?.get(chain.request())

    val now = System.currentTimeMillis()
    // (2) 获取缓存策略strategy
    // 工厂模式
    // networkRequest----->网络请求
    // cacheResponse------>缓存响应结果
    val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
    val networkRequest = strategy.networkRequest
    val cacheResponse = strategy.cacheResponse

    // (3) 从缓存中追踪Response
    cache?.trackResponse(strategy)
    val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE

    // (4) 如果缓存不适用,则关闭
    if (cacheCandidate != null && cacheResponse == null) {
      // The cache candidate wasn't applicable. Close it.
      cacheCandidate.body?.closeQuietly()
    }

    // (5) 如果网络被禁止,且缓存为空,则返回失败
    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)
          }
    }

    // (6)如果网络被禁止,从缓存中获取响应并返回
    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)
    }
    // (7) 调用下一个拦截器,即ConnectInteceptor
    // 从网络中获取响应response
    var networkResponse: Response? = null
    try {
      networkResponse = chain.proceed(networkRequest)
    } finally {
      // 关闭Body,防止出现内存泄漏
      if (networkResponse == null && cacheCandidate != null) {
        cacheCandidate.body?.closeQuietly()
      }
    }
    //--------------------------------------------------------------------
    //--------------------------------------------------------------------
    // //  处理Connect拦截器返回的网络响应Response  
    //--------------------------------------------------------------------
    //--------------------------------------------------------------------
    // (8) 如果缓存中存在Response,同时检测networkResponse是否被修改
    // 然后再更新缓存中的数据到最新
    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()
        cache!!.trackConditionalCacheHit()
        // 更新缓存中的数据到最新
        cache.update(cacheResponse, response)
        return response.also {
          listener.cacheHit(call, it)
        }
      } else {
        cacheResponse.body?.closeQuietly()
      }
    }

    // (9) 使用网络响应networkResponse和cacheResponse
    // 构建response
    val response = networkResponse!!.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build()

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

      if (HttpMethod.invalidatesCache(networkRequest.method)) {
        try {
          cache.remove(networkRequest)
        } catch (_: IOException) {
          // The cache cannot be written.
        }
      }
    }
    // (10) 将(9)构建的response返回给上一个拦截器
    // 即桥接拦截器BridgeInterceptor
    return response
  }

(4)ConnectInterceptor:连接拦截器,用于打开一个Socket连接,去连接目标服务器;

  • a. 获取一个ExChange对象,即Http2ExchangeCodec,主用于对HTTP请求编码,发起Socket连接,并解码HTTP响应;
  • b. 执行下一个拦截器CallServerInterceptor,并直接返回其response。

源码如下

 /**
   * 4-4 ConnectInterceptor拦截器
   * 打开一个连接,去连接目标服务器
   */
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    // (1) 获取一个ExChange对象,该对象是对Http1ExchangeCodec/Http2ExchangeCodec和下一个Chain的封装
    //  Http1ExchangeCodec/Http2ExchangeCodec分别对应于HTTP1.1/HTTP2.0
    // 主要用户对HTTP请求编码,发起Socket连接,并解码HTTP响应

    // Socket通信过程:
    // a. 发送网络请求头部headers【writeRequest】;
    // b. 打开一个接收器sink,写网络请求body【newKnownLengthSink或newChunkedSink】;
    // c. 完成写入,并关闭接收器sink;
    // d. 读取响应头部【readResponseHeaders】;
    // e. 打开数据源source,读取响应实体body【newFixedLengthSource】
    // f. 读取完毕,关闭数据源Source
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange = exchange)

    // (2) 执行下一个拦截器CallServerInterceptor
    // 并将该拦截器的response作为本拦截器(ConnectInterceptor)的response返回给上一个拦截器
    return connectedChain.proceed(realChain.request)
  }

(5)CallServerInterceptor:请求服务器拦截器,拦截器链的最后一个拦截器,执行通过网络请求服务器,即完成Socket通信。

  • a. 获取网络访问对象Http1ExchangeCodec/Http2ExchangeCodec;
  • b. 获取网络访问请求request和body;
  • c. 打开一个Socket连接,获取一个sink对象,写入头部;
  • d. 执行Socket数据通信;
  • e. 解析Socket通信响应头部信息exchange.readResponseHeaders;
  • f. 构建最终的网络响应response,返回给上一个拦截器ConnectInterceptor。

源码如下

/**
   * 4-5 CallServerInterceptor拦截器
   * 拦截器链的最后一个拦截器,执行通过网络请求服务器,即完成Socket通信
   */
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    // (1) 获取网络访问对象Http1ExchangeCodec/Http2ExchangeCodec
    val exchange = realChain.exchange!!
    // (2) 获取网络访问请求request和body
    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 {
      // (3) 打开一个Socket连接
      // 获取一个sink对象,写入头部
      exchange.writeRequestHeaders(request)

      // (4) 检查是否有body的请求方法
      // 即请求方法不是GET和HEAD的情况
      if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
        //如果请求头是"100-continue",等待服务器的响应
        if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
          exchange.flushRequest()
          responseBuilder = exchange.readResponseHeaders(expectContinue = true)
          exchange.responseHeadersStart()
          invokeStartEvent = false
        }
        // 写入body,发送请求数据到服务端
        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
    }

    try {
      // (5) 读取响应头
      if (responseBuilder == null) {
        responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
        if (invokeStartEvent) {
          exchange.responseHeadersStart()
          invokeStartEvent = false
        }
      }
      // (6) 构建响应Response
      var response = responseBuilder
          .request(request)
          .handshake(exchange.connection.handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build()
      var code = response.code
      // (7) 如果服务器返回的状态码是100,再次尝试读取具体的response
      if (code == 100) {
        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)
      // (8) 如果是WebSocket,并且返回状态码为101,表示响应body为空
      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 {
        // (9) 读取响应实体body
        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()}")
      }
      // (10) 将最终的网络响应response返回给上一层拦截器ConnectInterceptor
      return response
    } catch (e: IOException) {
      if (sendRequestException != null) {
        sendRequestException.addSuppressed(e)
        throw sendRequestException
      }
      throw e
    }
  }
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页