OKHttp源码精髓之拦截器分析

上一篇文章我们知道okhttp异步真正执行的实际是RealCall这个类的 getResponseWithInterceptorChain 方法
Response getResponseWithInterceptorChain() throws IOException {
    // 拦截器的一个集合
    List<Interceptor> interceptors = new ArrayList<>();
    // 客户端的所有自定义拦截器
    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);
  }
RetryAndFollowUpInterceptor拦截器

直接看intercept的源码

 @Override public Response intercept(Chain chain) throws IOException {
    //获得上一个的request
    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
        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();
        }
      }
     //默认priorResponse为空
      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;
      }
      // 请求就变为了重试的请求 Request
      request = followUp;
      priorResponse = response;
    }
  }

followUpRequest源码分析

switch (responseCode) {
 case HTTP_TEMP_REDIRECT://307
       //如果不是get方法且不是head方法直接返回一个空
        if (!method.equals("GET") && !method.equals("HEAD")) {
          return null;
        }

 case HTTP_SEE_OTHER:
        if (!client.followRedirects()) return null;
        // 从头部信息里面 获取 Location 新的链接
        String location = userResponse.header("Location");
        if (location == null) return null;
        // 生成一个新的 HttpUrl
        HttpUrl url = userResponse.request().url().resolve(location);
        // 返回一个新链接的 Request
        return requestBuilder.url(url).build();
}

**处理重试的一个拦截器,会去处理一些异常,只要不是致命的异常就会重新发起一次请求(把Request给下级),如果是致命的异常就会抛给上一级;
会处理一些重定向等等,比如 3XX 307、407 就会从头部中获取新的路径,生成一个新的请求交给下一级(重新发起一次请求)**

BridgeInterceptor桥接拦截器:编码,压缩等添加头部作用
@Override public Response intercept(Chain chain) throws IOException {
   //获得上一次的请求
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
     //获得请求参数
    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }
      //长度
      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    // 这个头信息必须要,但是使用者不一定会自己设置
    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

    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }
   //保存cookie信息
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
    // 下一级给我们的 Response
    Response networkResponse = chain.proceed(requestBuilder.build());
    // 处理保存 Cookie
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    // 如果后台返回的数据采用 gzip 压缩了
    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      // GzipSource okio 的输入流
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }
    // 我们这个拦截器处理的 Response 返回回去
    return responseBuilder.build();
  }

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

CacheInterceptor缓存拦截器
@Override public Response intercept(Chain chain) throws IOException {
    // 从缓存里面拿
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
    // 构建了一个缓存策略  ①,networkRequest 不为空,且设置只从缓冲中获取,两者此时都设置为空
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    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());
      }
    }

    // 处理返回,处理 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;
  }

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();①进行分析

public Factory(long nowMillis, Request request, Response cacheResponse) {
      this.nowMillis = nowMillis;
      this.request = request;
      this.cacheResponse = cacheResponse;
      //如果缓存不等于空
      if (cacheResponse != null) {
        this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
        //获得缓存的头部
        Headers headers = cacheResponse.headers();
        //头部信息for循环
        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);
          }
        }
      }
    }
//get源码
public CacheStrategy get() {
     //判断是不是要缓存
      CacheStrategy candidate = getCandidate();
      // onlyIfCached 只能从缓存里面获取
      // networkRequest = null cacheResponse = null
      if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // 这时候netwrodRequest和cacheResponse都为空.
        return new CacheStrategy(null, null);
      }

      return candidate;
    }

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

ConnectInterceptor源码分析
 @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");
    // ①源码分析
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    // 拿到一个连接
    RealConnection connection = streamAllocation.connection();
    // 把这个连接传给下一级
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

* HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);*

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

findHealthyConnection源码分析我们可以知道主要的方法就是findConnection

RealConnection result = null;

 if (result == null) {
       //如果为空,从缓存池中获得连接
        Internal.instance.get(connectionPool, address, this, null);
        if (connection != null) {
          foundPooledConnection = true;
          result = connection;
        } else {
          selectedRoute = route;
        }
      }
    }
  // 没有找到可用的连接,自己new一个
        result = new RealConnection(connectionPool, selectedRoute);
        acquire(result, false);
    // 真正建立连接 RealConnection -> connect
    result.connect(
        connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());

result.connect这里面主要的代码 connectSocket(connectTimeout, readTimeout, call, eventListener);

try {
   //看这个
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
      ce.initCause(e);
      throw ce;
    }
public void connectSocket(Socket socket, InetSocketAddress address,
      int connectTimeout) throws IOException {
      //由此我们可以知道,okhttp是根据socket进行连接的
    socket.connect(address, connectTimeout);
  }

看下②的 HttpCodec resultCodec = 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);
    }
  }

findHealthyConnection() 找一个连接,首先判断有没有健康的,没有就创建(建立Scoket,握手连接),连接缓存得到一条结论:OkHttp 是基于原生的 Socket + okio(原生IO的封装)
封装 HttpCodec 里面封装了 okio 的 Source(输入) 和 Sink (输出),我们通过 HttpCodec 就可以操作 Socket的输入输出,我们就可以像服务器写数据和读取返回数据

CallServerInterceptor写数据拦截器
 @Override public Response intercept(Chain chain) throws IOException {
    realChain.eventListener().requestHeadersStart(realChain.call());
    // 写一些请求头的数据
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {    
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        realChain.eventListener().responseHeadersStart(realChain.call());
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        realChain.eventListener().requestBodyStart(realChain.call());
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        // 写数据,表单数据,文件
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
      } else if (!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.
        streamAllocation.noNewStreams();
      }
    }

    httpCodec.finishRequest();

    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
    // 返回一个 Response
    Response response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();

    realChain.eventListener()
        .responseHeadersEnd(realChain.call(), response);

    int code = response.code();
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }
   //204代表成功访问服务器但是没有内容返回
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

写数据和读取数据
写头部信息,写body表单信息等等

连接三个核心类(连接复用)

RealConnection、ConnectionPool、StreamAllocation
RealConnection: 建立连接的一个对象的封装
ConnectionPool:保存了连接
StreamAllocation: 找一些连接,做一下封装

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值