Okhttp之拦截器

一,源码精髓责任链模式分析

1. 最终执行到AsyncCall这个类的eecute这个方法,在这个方法中真正的开始网络操作,并将它们通过回调返回给网络请求者

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

1.1 先来看着一行代码 Response response = getResponseWithInterceptorChain();   我们通过getResponseWithInterceptorChain方法去得到一个响应,这个方法内部置有很多拦截器,这些拦截器内部各自对请求做了一些操作,然后包装成响应返回。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 拦截器的一个集合
    List<Interceptor> interceptors = new ArrayList<>();
    // 客户端的所有自定义拦截器(我们在Request中自定义的)
    interceptors.addAll(client.interceptors());// 自己的
    // OKhttp 5 个拦截器 ,责任链设计模式,每一个拦截器只处理与他相关的部分 volley
    interceptors.add(retryAndFollowUpInterceptor);// 重试
    interceptors.add(new BridgeInterceptor(client.cookieJar()));// 基础
    interceptors.add(new CacheInterceptor(client.internalCache()));// 缓存
    interceptors.add(new ConnectInterceptor(client));// 建立连接
    interceptors.add(new CallServerInterceptor(forWebSocket));// 写数据

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

1.2 Intercepter 是一个接口,内部还又一个子接口 chain

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

1.3 RealInterceptorChain这个类是Chain接口的实现类将客户端和OkhttpCliet定义的拦截器添加到一个集合中List<Interceptor> interceptors,再将这个集合添加到RealInterceptorChain里面,然后通过process这个方法 ,依照集合的顺序执行这些插值器,并最终封装成一个response返回给RealCall,RealCall再通过回调将response返回给网络请求者。

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    if (response.body() == null) {
      throw new IllegalStateException(
          "interceptor " + interceptor + " returned a response with no body");
    }

    return response;
  }

二我们来看一下Okhttp的五种拦截器

1.RetryAndFollowUpIntercepter  (重试)

 @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();

    streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
            call, eventListener, callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      // 一个死循环 ,重试, 两种情况可以终止 return Response, throws IOException
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response;
      boolean releaseConnection = true;
      try {
        // 丢给下一个拦截器去处理,会有异常的情况
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        // 先处理 RouteException
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // 处理 IOException
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // 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();
      }

      // 从下面过了一个 Response ,但是这个 Response 不能够直接返回给上一级,会有重定向
      // 重定向 返回码是 307 ,从头部的 Location 里面获取新的链接 重新请求一次
      Request followUp = followUpRequest(response);

      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        // 直接返回给上一级
        return response;
      }

      closeQuietly(response.body());

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(client.connectionPool(),
            createAddress(followUp.url()), call, eventListener, callStackTrace);
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      // 请求就变为了重试的请求 Request
      request = followUp;
      priorResponse = response;
    }
  }

1.1处理重试的一个拦截器,会处理一些异常,只要不是致命的异常就会重新发一次请求(把request发给下级),如果是致命的异常就会抛给上一级(即调用者)

1.1.1 再RealCall的getResponseWithInterceptorChain这个方法中,调用chain.proceed(originalRequest)这个方法就可以从第一个开始只能怪Interceptor了,轮回执行拦截器的代码再RealInterceptor 中的process方法中 如下:

 RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();


  RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
            connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
            writeTimeout);
    //这里 index + 1 是说的是此时传入的索引为1 但是当前的index = 0
                                          //2                  1
                                          //3                  2
                                          //4                  3
                                          //5                  4   执行完这一次 应为5>=5 所以爆出异常,不能再执行下去了
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

 return response;

1.1.2 第一个执行的拦截器就是RetryAndFollowUpIntercepter 

   1.1.2.1 第一步会先从上一级获得request

//从上一级获取Request(因为是同一块内存,所以收到的request经过拦截器处理处理之后的Request)
    Request request = chain.request();

   1.1.2.2  注意这俩个参数 ,后面还会用到

 int followUpCount = 0;  //记录重定向的次数
    //这个是重试拦截器最终的响应,并且且这个响应会回调给调用者
    Response priorResponse = null;

   1.1.2.3 再之后是一个while的死循环, 这个死循环只有再这两种情况下才可以终止 一是 :return Response,  二是 :throws IOException

    然后执行

// 这是执行的第一个拦截器,丢给下一个拦截器去处理,会有异常的情况
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;

  如果发生异常,就执行:  分致命异常和非致命异常俩种情况。

 // 先处理 RouteException
        if (!recover(e.getLastConnectException(), false, request)) {
          //如过拿到的是一个致命的异常,就返回上一级
          throw e.getLastConnectException();
        }

        releaseConnection = false;
        //如果不是致命的异常,就再执行一遍上述代码
        continue;
      } catch (IOException e) {
        // 处理 IOException
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;

      不论请求是否发生异常,最后都会执行finnaly中的语句

     finally {
      
       if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      // 从下面过了一个 Response ,但是这个 Response 不能够直接返回给上一级,会有重定向
      // 重定向 返回码是 307 ,从头部的 Location 里面获取新的链接 重新请求一次
      Request followUp = followUpRequest(response);

      if (followUp == null) {
        //等于空,说明不需要处理状态码,直接返回就可以了
        if (!forWebSocket) {
          streamAllocation.release();
        }
        // 直接返回给上一级
        return response;
      }
  // 如果followUp不等于空,请求就变为了重试的请求 Request
      //因为这一段代码处于死循环,所以有一次开始执行这一段代码
      request = followUp;
      priorResponse = response;
 }

1.1.2.4 其中方法 Request followUp = followUpRequest(response); 是对状态码的一些处理

private Request followUpRequest(Response userResponse) throws IOException {
    if (userResponse == null) throw new IllegalStateException();
    Connection connection = streamAllocation.connection();
    Route route = connection != null
        ? connection.route()
        : null;
    int responseCode = userResponse.code();

    final String method = userResponse.request().method();
    // 对状态码进行判断

    switch (responseCode) {
      case HTTP_PROXY_AUTH:
        Proxy selectedProxy = route != null
            ? route.proxy()
            : client.proxy();
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
        }
        return client.proxyAuthenticator().authenticate(route, userResponse);

      case HTTP_UNAUTHORIZED:
        return client.authenticator().authenticate(route, userResponse);

      case HTTP_PERM_REDIRECT:
      case HTTP_TEMP_REDIRECT:
        // "If the 307 or 308 status code is received in response to a request other than GET
        // or HEAD, the user agent MUST NOT automatically redirect the request"
        if (!method.equals("GET") && !method.equals("HEAD")) {
          return null;
        }
        // fall-through
      case HTTP_MULT_CHOICE:
      case HTTP_MOVED_PERM:
      case HTTP_MOVED_TEMP:
      case HTTP_SEE_OTHER:
        // Does the client allow redirects?
        if (!client.followRedirects()) return null;
        // 从头部信息里面 获取 Location 新的链接
        String location = userResponse.header("Location");
        if (location == null) return null;
        // 生成一个新的 HttpUrl
        HttpUrl url = userResponse.request().url().resolve(location);

        // Don't follow redirects to unsupported protocols.
        if (url == null) return null;

        // If configured, don't follow redirects between SSL and non-SSL.
        boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
        if (!sameScheme && !client.followSslRedirects()) return null;

        // Most redirects don't include a request body.
        Request.Builder requestBuilder = userResponse.request().newBuilder();
        if (HttpMethod.permitsRequestBody(method)) {
          final boolean maintainBody = HttpMethod.redirectsWithBody(method);
          if (HttpMethod.redirectsToGet(method)) {
            requestBuilder.method("GET", null);
          } else {
            RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
            requestBuilder.method(method, requestBody);
          }
          if (!maintainBody) {
            requestBuilder.removeHeader("Transfer-Encoding");
            requestBuilder.removeHeader("Content-Length");
            requestBuilder.removeHeader("Content-Type");
          }
        }

        // When redirecting across hosts, drop all authentication headers. This
        // is potentially annoying to the application layer since they have no
        // way to retain them.
        if (!sameConnection(userResponse, url)) {
          requestBuilder.removeHeader("Authorization");
        }
        // 返回一个新链接的 Request
        return requestBuilder.url(url).build();

      case 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;
        }

        if (userResponse.request().body() instanceof UnrepeatableRequestBody) {
          return null;
        }

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

        return userResponse.request();

      default:
        return null;
    }
  }

2.BridgeInterceptor

    做一个简单的处理,设置一些通用的请求头,Content-Type, Connection,Content-Length ,Cookie  做一些返回处理,如果返回的数据被压缩了,采用ZIPSource, 保存Cookie。

3 . CacheIntercepter

3.1 再缓存可用的情况下,会取读取本地的缓存数据,如果没有直接去服务器,如果有,首先判断有没有缓存策略,然后判断有没有过期,如果没有过期直接那缓存,如果过期需要添加一些之前的头部信息如If-Modified-since,这个时候后台服务器有可能会返回304,代表你还是可以拿本地缓存,每次读取到新的响应后做一次缓存。

@Override public Response intercept(Chain chain) throws IOException {
    // 从缓存里面拿,到底怎么拿?放一边
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
    // 构建了一个缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // networkRequest 还不知道是啥 networkRequest 做了处理
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    if (cache != null) {
      cache.trackResponse(strategy);
    }

    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    // If we're forbidden from using the network and the cache is insufficient, fail.
    // 如果请求的 networkRequest 是空,缓存的 cacheResponse 是空我就返回 504
    // 指定该数据只从缓存获取,一般不会这么傻
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // If we don't need the network, we're done.
    if (networkRequest == null) {
      // 如果缓存策略里面的 networkRequest 是空,那么就 返回 缓存好的 Response
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
      // 否则的话给下面
      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) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    // 处理返回,处理 304 的情况
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        // 服务器数据没有变化,你还是可以拿之前的缓存
        Response 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;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
    // 要返回 response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (cache != null) {
      // 然后缓存获取的 Response
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

    return response;
  }

 3.2 这个方法的第一步 是从缓存中获取响应

// 从缓存里面拿,到底怎么拿?放一边
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

3.3第二步是构建一个缓存策略

long now = System.currentTimeMillis();
    // 构建了一个缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // networkRequest 还不知道是啥 networkRequest 做了处理
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

3.3.1解析这一句  CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();

3.3.1.1  看CacheStrategy的Factory(工厂模式)这个方法

//nowMillis  开始请求缓存的时间
//最原始的请求
//从缓存当中获取的缓存响应
public Factory(long nowMillis, Request request, Response cacheResponse) {
      this.nowMillis = nowMillis;   //这个当前的事件
      this.request = request;       //这个是原始的request
      this.cacheResponse = cacheResponse;  

      if (cacheResponse != null) {
        
        
        this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
        Headers headers = cacheResponse.headers();
        for (int i = 0, size = headers.size(); i < size; i++) {
          String fieldName = headers.name(i);
          String value = headers.value(i);
          // 解析之前缓存好的一些头部信息,服务器给的 Expires 缓存的过去时间,Last-Modified 服务器上次数据的更新时间
          if ("Date".equalsIgnoreCase(fieldName)) {
            servedDate = HttpDate.parse(value);
            servedDateString = value;
          } else if ("Expires".equalsIgnoreCase(fieldName)) {
            expires = HttpDate.parse(value);
          } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
            lastModified = HttpDate.parse(value);
            lastModifiedString = value;
          } else if ("ETag".equalsIgnoreCase(fieldName)) {
            etag = value;
          } else if ("Age".equalsIgnoreCase(fieldName)) {
            ageSeconds = HttpHeaders.parseSeconds(value, -1);
          }
        }
      }
    }

3.3.1.2  再来看 get();方法

   public CacheStrategy get() {

      //通过getCandidate()这个方法去 调整和选择缓存的策略
      CacheStrategy candidate = getCandidate();

      // 除了 这种 onlyIfCached 只能从缓存里面获取响应的策略
      // networkRequest = null cacheResponse = null
      if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // We're forbidden from using the network and the cache is insufficient.
        return new CacheStrategy(null, null);
      }

      //其他的直接将这种策略返回就可以了
      return candidate;
    }

3.3.1.3   再来看看 getCandidate() 方法

 private CacheStrategy getCandidate() {

       //第一种策略
         // 如果没有缓存的话
      if (cacheResponse == null) {
         //直接返回 让响应 这样在全局中 networkRequest=request cacheResponse=null
        return new CacheStrategy(request, null);
      }


        //第二种策略
        // 要不要缓存,缓存策略,Public、private、no-cache、max-age
      if (!isCacheable(cacheResponse, request)) {  //不需要缓存
        return new CacheStrategy(request, null);
      }

      //第三种策略 noCache
      CacheControl requestCaching = request.cacheControl();
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

      //第四种策略 onlyIfCached
      CacheControl responseCaching = cacheResponse.cacheControl();
      if (responseCaching.immutable()) {
        return new CacheStrategy(null, cacheResponse);
      }

      
      // 缓存策略 + 过期时间  public private max-Age 
      long ageMillis = cacheResponseAge();
      long freshMillis = computeFreshnessLifetime();
      if (requestCaching.maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }
      long minFreshMillis = 0;
      if (requestCaching.minFreshSeconds() != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }
      long maxStaleMillis = 0;
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }

      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        // 如果缓存没有过期并且需要缓存 那么 networkRequest = null cacheResponse = 缓存好的
        return new CacheStrategy(null, builder.build());
      }

      // 本地有缓存,并且缓存已经过去,那么需要把上一次的请求头的一些字段带过去 If-Modified-Since
      // 请求 -> 返回 -> 更新时间
      // 请求(If-Modified-Since) -> 比对(304)

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      String conditionName;
      String conditionValue;
      if (etag != null) {
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        conditionName = "If-Modified-Since";
        conditionValue = servedDateString;
      } else {
        return new CacheStrategy(request, null); // No condition! Make a regular request.
      }

      Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
      Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

      Request conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build();
      return new CacheStrategy(conditionalRequest, cacheResponse);
    }

3.4 确定了缓存策略之后,再次回到CacheInterceptor

   // 如果请求的 networkRequest 是空,缓存的 cacheResponse 是空我就返回 504
    // 指定该数据只从缓存获取,一般不会这么傻
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

3.5只从缓存中读取,并在请求网络

  if (networkRequest == null) {
      //在缓存策略中将本来不是空的request替换成了为空的networkRequest
      //这样就不会再去请求网络了
      // 如果缓存策略里面的 networkRequest 是空,那么就 返回 缓存好的 Response
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

3.6如果networkRequest这个之没有在CacheStratedgy中被缓存策略重置为null的话,说明缓存策略希望我们能够继续去请求网络

    Response networkResponse = null;
    try {
      // 否则的话给下面
      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) {
        closeQuietly(cacheCandidate.body());
      }
    }

3.7上面这个方法,将请求传递给下级拦截器继续请求网络的返回值可能是一个304等异常的网页,所以需要以下处理 如果是HTTP_NOT_MODIFIED(304) ,说明服务器端的数据没有发生过改变,我们依然可以读取缓存中的数据。

// 处理返回,处理 304 的情况
    if (cacheResponse != null) {
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        // 服务器数据没有变化,你还是可以拿之前的缓存
        Response 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;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }

3.8 要返回给上一级拦截器的响应

 // 要返回 response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

3.9 将请求得到的响应缓存一份

if (cache != null) {
      // 然后缓存获取的 Response
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

4.ConnectInterceptor

  newStream ——>findHealthyConnection() 首先判断有没有健康的,如果没有就创建(建立Socket,握手连接),连接缓存

  得到一条结论: OkHttp 是基于原生的Socket

  封装HttpCode 里面封装了 okio的Source(输入)和Sink(输出),我们通过HttpCode就可以操作Socket的输入输出,我们就可以向服务器写数据和读取返回数据。

4.1 在ConnectionInterceptor 的intercept方法

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    // newStream -> HttpCodec
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    // 拿到一个连接
    RealConnection connection = streamAllocation.connection();
    // 把这个连接传给下一级
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

4.1.1通过newStream()方法去找去实例化HttpCode这个封装了okio的接口

public HttpCodec newStream(
      OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
    int connectTimeout = chain.connectTimeoutMillis();
    int readTimeout = chain.readTimeoutMillis();
    int writeTimeout = chain.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
      // 找一个连接,首先判断有没有健康的,没有就创建(建立Scoket,握手连接),连接缓存
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
      // 封装 HttpCodec 里面封装了 okio 的 Source(输入) 和 Sink (输出)
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }

4.1.2通过findHealthyConnection 去找一个健康的连接

private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          connectionRetryEnabled);

      // If this is a brand new connection, we can skip the extensive health checks.
      synchronized (connectionPool) {
        if (candidate.successCount == 0) {
          return candidate;
        }
      }

      // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
      // isn't, take it out of the pool and start again.
      if (!candidate.isHealthy(doExtensiveHealthChecks)) {
        noNewStreams();
        continue;
      }

      return candidate;
    }
  }

4.1.3通过resultConnection.newCodec(client, chain, this);这个方法

 public HttpCodec newCodec(OkHttpClient client, Interceptor.Chain chain,
      StreamAllocation streamAllocation) throws SocketException {
    if (http2Connection != null) {
      return new Http2Codec(client, chain, streamAllocation, http2Connection);
    } else {
      socket.setSoTimeout(chain.readTimeoutMillis());
      // Okio : 基于原生的IO的封装 ,IO涉及类和方法忒多,source(输入) sink(输出)
      source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
      sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
      // Http1Codec 就是 封装了 source 和 sink 就是自己的输入输出流,本质就是操作 Socket 的输入输出流
      return new Http1Codec(client, streamAllocation, source, sink);
    }
  }

      在此之前 Socket和okio 进行了绑定

     String maybeProtocol = connectionSpec.supportsTlsExtensions()
          ? Platform.get().getSelectedProtocol(sslSocket)
          : null;
      socket = sslSocket;
      source = Okio.buffer(Okio.source(socket));
      sink = Okio.buffer(Okio.sink(socket));

5.CallServerIntercepter

  写数据和读取数据。

  写头部信息

 // 写一些请求头的数据
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

和表单

      // 写数据,表单数据,文件
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        realChain.eventListener()

6.连接的三个核心类

RealConnection ,

ConnectionPool,

StreamAlocation

猜你喜欢

转载自blog.csdn.net/lyfxh1314/article/details/86536605