深刻OKHttp源码分析(三)----okhttp拦截器解析

本篇咱们就开始分析okhttp中的重要组成部分--拦截器,那么,什么是拦截器呢,咱们看下官网的说明 Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls. 翻译过来就是,拦截器是一种强大的机制,能够实现网络监听,请求以及响应重写,请求失败重试等功能,拦截器是不区分同步和异步的。 拦截器是能够自定义的,自定义拦截器又可分为应用程序拦截器和网络拦截器,不一样拦截器功能又有很大不一样,不过这里咱们今天不打算写自定义拦截器,其实自定义拦截器的原理和okhttp内置的五种拦截器是同样的,咱们这篇文章是分析拦截器的内部原理,因此今天咱们只讲解okhttp内置的5种拦截器,有兴趣的同窗能够去okhttp官网查看自定义拦截器的功能和实现,都有很详细的讲解,官网看这里:拦截器 下面开始咱们今天的源码分析html

####OkHttp拦截器流程 在okhttp中,内置的5中拦截器分别是RetryAndFollowupInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、CallServerInterceptor,执行的流程是这样的 RetryAndFollowupInterceptor->BridgeInterceptor->CacheInterceptor->ConnectInterceptor->CallServerInterceptor 咱们用代码来证实:java

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    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);
  }
复制代码

还记得getResponseWithInterceptorChain() 方法吗?在前面的文章中咱们见过了这个方法,拦截器集合中拦截器的添加的顺序就是他们的执行顺序,interceptors.addAll(client.interceptors());是添加自定义的应用程序拦截器,git

if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
复制代码

这个是添加非webSocket状况下的全部自定义的网络拦截器。其他的拦截器都是okhttp内置的拦截器,有什么能证实拦截器的执行顺序是按照添加的顺序来的呢,OK,让咱们往下看github

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

这个是建立真正的拦截器链,看到传入的拦截器集合没有,看到了,好的,咱们接着往下看,web

return chain.proceed(originalRequest);
复制代码

这里调用了拦截器链的proceed方法,这里面就开始拦截器的链式调用了,咱们进去看看缓存

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

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

    calls++;
    ·····//省略部分代码
    // 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);
    ···//省略部分代码
    return response;
  }
复制代码

能够看到,proceed方法里面调用了一个重载的proceed方法,咱们继续看,能够看到这个proceed方法中,从新生成了一个新的拦截器链,咱们看第五个参数是index+1,指的是从interceptors参数中的index+1索引的位置向后取拦截器,将这些拦截器从新生成一个新的拦截器,bash

Interceptor interceptor = interceptors.get(index);
复制代码

这行代码就是获取当前的拦截器,服务器

Response response = interceptor.intercept(next);
复制代码

这行代码是调用当前拦截器的intercept方法,将新的拦截器链传入,获得咱们要的response对象,这个方法的内部,就是链式调用拦截器链中的拦截器,具体信息咱们下面逐个拦截器进行分析,首先咱们来分析拦截器链中的第一个拦截器RetryAndFollowUpInterceptor #####OkHttp拦截器之RetryAndFollowUpInterceptor解析 说到这个拦截器,我想你们应该不是那么陌生,由于前面咱们已经见到过了,具体哪里呢,咱们来看下cookie

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }
复制代码

看最后一行,就是这个拦截器的建立,原来这样子,在建立Call对象的时候就已经建立了这个拦截器实例。这样在建立拦截器链的时候就不用再建立了,经过上面的分析咱们知道,在拦截器链的proceed方法中,先调用拦截器链中的第一个拦截器的intercept方法,前面讲过,第一个拦截器是RetryAndFollowUpInterceptor拦截器,那么咱们就进入到这个拦截器的intercept方法中看看网络

@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 streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      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.
        if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, streamAllocation, 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(); } Request followUp = followUpRequest(response, streamAllocation.route()); 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); this.streamAllocation = streamAllocation; } else if (streamAllocation.codec() != null) { throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?"); } request = followUp; priorResponse = response; } } 复制代码

咱们从上往下开始分析,先是获取拦截器链的Request对象,而后将传进来的拦截器链转换成真正的拦截器链,并获取拦截器链中的Call对象,这个对象是在拦截器链建立的时候就已经赋值进去啦,接着咱们看下streamAllocation对象,这里将OkHttpClient的链接池,还有请求的url的地址信息,请求对象call,事件监听传递进去,生成了一个分配的流对象,用于和服务器传递数据。 接着咱们看下面的while循环,首先判断这个请求是否被取消了,若是是被取消了,就关闭流,并抛出异常。 而后在下面的try代码块中,咱们看到了真正的拦截器链又调用了proceed方法来获取咱们想要的response对象,可见,真正获得response对象并非在RetryAndFollowUpInterceptor拦截器中,这个拦截器只是对response对象进行包装处理而已,接下来咱们继续往下看,在catch代码块中,咱们看到捕获了RouteException和io异常,并在某些条件下进行重试,也就是重连操做,最后在finally代码块中,判断若是抛出了异常,咱们就要进行资源的释放。 继续往下看,若是priorResponse对象不为null,就从新构建一个body内容为null的response,而后传递给followUpRequest方法生成重定向的Request对象,可是重定向的请求的生成也是须要条件的,那么咱们就进入到这个方法中看看

private Request followUpRequest(Response userResponse, Route route) throws IOException {
    if (userResponse == null) throw new IllegalStateException();
    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;

        String location = userResponse.header("Location");
        if (location == null) return null;
        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"); } 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;
        }

        if (retryAfter(userResponse, 0) > 0) {
          return null;
        }

        return userResponse.request();

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

        if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
          // specifically received an instruction to retry without delay
          return userResponse.request();
        }

        return null;

      default:
        return null;
    }
  }
复制代码

妈呀,这么多代码,看上去好恐怖啊,发抖,其实没什么,只要你们对HTTP状态码了解了,而后根据状态码来看便可,若是对状态码不了解,能够看这里状态码大全 言归正传,咱们继续看咱们的代码,咱们只须要重点关注case值为30x的状态码便可,由于只有30x的才是重定向有关的,咱们能够看到这部分代码从新根据传进来的response对象信息来生成了新的Request对象,只是进行了部分请求头的处理,其实这和咱们前面生成Request对象的性质是同样的,只不过这里由于是重定向,OkHttp就帮你生成了Request对象并进行了地址和请求头的处理而已,如今咱们回过头来看拦截器的intercept方法,

if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }
复制代码

这里判断重定向的Request对象followUp是否为空,若是为空说明不须要重定向操做,就直接将前面获得的response对象返回便可

closeQuietly(response.body());
复制代码

若是是重定向请求,才会执行到这里,这里是将以前的response对象的body进行关闭,由于其中含有流对象, 接着咱们看下面这块代码

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

这里是根据以前请求获得的response对象判断和当前重定向请求的url是否同样,若是不同,则进入if代码块中,将以前的流对象进行释放,并根据重定向请求从新生成新的分配流对象。 下面进行简单的赋值操做,再次执行while循环,也就再次执行下面一行代码

response = realChain.proceed(request, streamAllocation, null, null);
复制代码

分析到这里,咱们就将RetryAndFollowUpInterceptor拦截器的intercept方法分析完成,下面咱们来分析第二个拦截器BridgeInterceptor #####OkHttp拦截器之BridgeInterceptor解析 一样的,拦截器中最重要的方法就是intercept方法了,咱们来看看BridgeInterceptor拦截器的intercept方法

@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 // the transfer stream. boolean transparentGzip = false; if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) { transparentGzip = true; requestBuilder.header("Accept-Encoding", "gzip"); } 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 networkResponse = chain.proceed(requestBuilder.build()); HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers()); Response.Builder responseBuilder = networkResponse.newBuilder() .request(userRequest); if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) { 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))); } return responseBuilder.build(); } 复制代码

下面咱们来从上往下分析下,首先获得拦截器链中的Request请求对象,并获得Request的构造器,而后获取Request对象的请求体,接下来, 若是请求体不为null,那就获取请求体的Content-Type值,若是不为空,就将Request构造器的header中添加Content-Type属性,而后根据请求体的内容长度来设置Content-Length属性和Transfer-Encoding属性,下面的代码直到拦截器链调用proceed方法以前都是设置Request对象的header属性,下面咱们跳过这行代码继续往下分析,下面就是对响应体response进行处理了,判断是不是支持gzip压缩,若是支持gzip压缩而且响应头中格式为gzip格式,就对响应体进行解压缩处理,而后移除掉Content-Encoding属性和Content-Length属性,将处理过的header传入响应体的构造器,最后生成并返回处理过的response对象。 接下来咱们继续分析CacheInterceptor拦截器,要了解CacheInterceptor首先咱们先来了解下OkHttp的缓存策略 #####OkHttp缓存策略源码分析 首先咱们看下okhttp中如何使用缓存

OkHttpClient client = new OkHttpClient.Builder()
                .cache(new Cache(new File("cache"),20*1024*1024))
                .readTimeout(5, TimeUnit.SECONDS).build();

复制代码

在建立OkHttpClient的对象的时候,只须要调用构造器的cache方法,写入对应的参数便可,Cache构造函数中file表示缓存的目录,后面表示缓存的大小,咱们点进Cache类中看下,看下构造函数

Cache(File directory, long maxSize, FileSystem fileSystem) {
    this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
  }
复制代码

在构造函数中咱们看到了DiskLruCache,就应该明白了内部原来是用的DiskLruCache来进行缓存的,对于缓存来讲,最重要的就是put和get方法,那如今咱们分别来看下cache的put和get方法

@Nullable CacheRequest put(Response response) {
    String requestMethod = response.request().method();

    if (HttpMethod.invalidatesCache(response.request().method())) {
      try {
        remove(response.request());
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
      return null;
    }
    if (!requestMethod.equals("GET")) {
      // Don't cache non-GET responses. We're technically allowed to cache
      // HEAD requests and some POST requests, but the complexity of doing
      // so is high and the benefit is low.
      return null;
    }

    if (HttpHeaders.hasVaryAll(response)) {
      return null;
    }

    Entry entry = new Entry(response);
    DiskLruCache.Editor editor = null;
    try {
      editor = cache.edit(key(response.request().url()));
      if (editor == null) {
        return null;
      }
      entry.writeTo(editor);
      return new CacheRequestImpl(editor);
    } catch (IOException e) {
      abortQuietly(editor);
      return null;
    }
  }
复制代码

咱们从上到下开始分析,首先获取请求的方式,而后根据请求的方式去验证缓存中是否存在无效缓存,若是存在,就将以前的缓存删除,下面判断请求方式是否为get方式,若是不是get方式,就返回null,不进行缓存,这点很重要,缓存是只缓存get方式的请求。缓存其余方式的响应的话,要处理的细节较多,复杂性过高,弊大于利。 下面将response对象进行封装,封装成Entry实体对象,咱们看下Entry是如何对response对象进行封装的,看下其构造函数

Entry(Response response) {
      this.url = response.request().url().toString();
      this.varyHeaders = HttpHeaders.varyHeaders(response);
      this.requestMethod = response.request().method();
      this.protocol = response.protocol();
      this.code = response.code();
      this.message = response.message();
      this.responseHeaders = response.headers();
      this.handshake = response.handshake();
      this.sentRequestMillis = response.sentRequestAtMillis();
      this.receivedResponseMillis = response.receivedResponseAtMillis();
    }
复制代码

从上面代码中能够看到,Entry只是对response的header部分进行封装,并未对response的body部分进行处理,那么在哪里对body部分进行处理的呢,继续往下看,而后根据请求的url生成key,而后获取DiskLRUCache缓存的editor对象,Entry对象调用writeTo方法将Entry对象写入editor进行缓存,进入到writeTo方法中

public void writeTo(DiskLruCache.Editor editor) throws IOException {
      BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));

      sink.writeUtf8(url)
          .writeByte('\n');
      sink.writeUtf8(requestMethod)
          .writeByte('\n');
      sink.writeDecimalLong(varyHeaders.size())
          .writeByte('\n');
      for (int i = 0, size = varyHeaders.size(); i < size; i++) {
        sink.writeUtf8(varyHeaders.name(i))
            .writeUtf8(": ")
            .writeUtf8(varyHeaders.value(i))
            .writeByte('\n');
      }

      sink.writeUtf8(new StatusLine(protocol, code, message).toString())
          .writeByte('\n');
      sink.writeDecimalLong(responseHeaders.size() + 2)
          .writeByte('\n');
      for (int i = 0, size = responseHeaders.size(); i < size; i++) {
        sink.writeUtf8(responseHeaders.name(i))
            .writeUtf8(": ")
            .writeUtf8(responseHeaders.value(i))
            .writeByte('\n');
      }
      sink.writeUtf8(SENT_MILLIS)
          .writeUtf8(": ")
          .writeDecimalLong(sentRequestMillis)
          .writeByte('\n');
      sink.writeUtf8(RECEIVED_MILLIS)
          .writeUtf8(": ")
          .writeDecimalLong(receivedResponseMillis)
          .writeByte('\n');

      if (isHttps()) {
        sink.writeByte('\n');
        sink.writeUtf8(handshake.cipherSuite().javaName())
            .writeByte('\n');
        writeCertList(sink, handshake.peerCertificates());
        writeCertList(sink, handshake.localCertificates());
        sink.writeUtf8(handshake.tlsVersion().javaName()).writeByte('\n');
      }
      sink.close();
    }
复制代码

能够看到,writeTo方法并未对body部分进行缓存,那么只剩下最后一个return语句了,是在return中进行缓存的吗???咱们点进去看一下,

private final class CacheRequestImpl implements CacheRequest {
    private final DiskLruCache.Editor editor;
    private Sink cacheOut;
    private Sink body;
    boolean done;

    CacheRequestImpl(final DiskLruCache.Editor editor) {
      this.editor = editor;
      this.cacheOut = editor.newSink(ENTRY_BODY);
      this.body = new ForwardingSink(cacheOut) {
        @Override public void close() throws IOException {
          synchronized (Cache.this) {
            if (done) {
              return;
            }
            done = true;
            writeSuccessCount++;
          }
          super.close();
          editor.commit();
        }
      };
    }

    ·····
    @Override public Sink body() {
      return body;
    }
  }
复制代码

能够看到,里面有个body变量,这个body就是咱们的response的响应体,咱们的response响应体就是在这个CacheRequestImpl里面进行缓存操做的,咱们还看到了CacheRequestImpl是实现了CacheRequest接口,为何要实现这个接口呢,实际上是为了给咱们后面要讲的CacheInterceptor拦截器使用的,讲完了put方法咱们再来看下Cache的get方法

@Nullable Response get(Request request) {
    String key = key(request.url());
    DiskLruCache.Snapshot snapshot;
    Entry entry;
    try {
      snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }

    try {
      entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }

    Response response = entry.response(snapshot);

    if (!entry.matches(request, response)) {
      Util.closeQuietly(response.body());
      return null;
    }

    return response;
  }
复制代码

在get方法的上面咱们能够看到,首先经过调用key方法经过请求的url地址获取缓存的key值,而后经过DiskLRUCache对象获取一个缓存的快照snapshot,接下来,将snapshot中的数据封装成Entry实体对象,而后调用Entry对象的response获得咱们要的response对象,咱们看下Entry的response方法是这么实现的

public Response response(DiskLruCache.Snapshot snapshot) {
      String contentType = responseHeaders.get("Content-Type");
      String contentLength = responseHeaders.get("Content-Length");
      Request cacheRequest = new Request.Builder()
          .url(url)
          .method(requestMethod, null)
          .headers(varyHeaders)
          .build();
      return new Response.Builder()
          .request(cacheRequest)
          .protocol(protocol)
          .code(code)
          .message(message)
          .headers(responseHeaders)
          .body(new CacheResponseBody(snapshot, contentType, contentLength))
          .handshake(handshake)
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(receivedResponseMillis)
          .build();
    }
复制代码

从上面代码中咱们能够看到,首先经过构造器将Request对象构造出来,而后调用Response类的构造器将response对象构造出来。回过头来咱们再看下get方法,而后将request对象和获得的response对象进行匹配,看看是否成立,若是不成立就返回null。 最后将从缓存中获得的response对象返回出来。 #####OkHttp拦截器之CacheInterceptor解析 接着咱们分析CacheInterceptor拦截器的intercept方法

@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();
    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.
    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) {
      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. 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 = networkResponse.newBuilder() .cacheResponse(stripBody(cacheResponse)) .networkResponse(stripBody(networkResponse)) .build(); if (cache != null) { 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; } 复制代码

妈呀,这么长,简直要懵逼,好怕怕,····,但是怕仍是要看,咱们开始从头开始分析, 首先根据拦截器链中的request对象从缓存中尝试获取缓存的response对象,接着往下看,CacheStrategy,???这是什么鬼,无奈英文不行,查下词典,CacheStrategy:缓存策略,好吧,猜测应该就是关于如何处理缓存的类,咱们打开看看,上来先看到这部分代码

/** The request to send on the network, or null if this call doesn't use the network. */ public final @Nullable Request networkRequest; /** The cached response to return or validate; or null if this call doesn't use a cache. */
  public final @Nullable Response cacheResponse;

  CacheStrategy(Request networkRequest, Response cacheResponse) {
    this.networkRequest = networkRequest;
    this.cacheResponse = cacheResponse;
  }
复制代码

看到两个变量networkRequest、cacheResponse,看看变量的注释,好像是说一个是使用网络时的request对象,一个是缓存的response对象。好像明白了,应该是指的是具体是经过网络获取仍是从缓存中获取咱们想要的response对象,若是是从网络中获取就是使用networkRequest,若是是从缓存中获取,就是使用cacheResponse。好的,大概明白了这个缓存策略类的做用,回过头来咱们再来看intercept方法,咱们看下这个缓存策略是如何获得的,进入get方法,

public CacheStrategy get() {
      CacheStrategy candidate = getCandidate();

      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; } 复制代码

只有简单几行代码,看得出来核心就是第一行代码,咱们进入到getCandidate方法中看下实现,

/** Returns a strategy to use assuming the request can use the network. */
    private CacheStrategy getCandidate() {
      // No cached response.
      if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }

      // Drop the cached response if it's missing a required handshake. if (request.isHttps() && cacheResponse.handshake() == null) { return new CacheStrategy(request, null); } // If this response shouldn't have been stored, it should never be used
      // as a response source. This check should be redundant as long as the
      // persistence store is well-behaved and the rules are constant.
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl requestCaching = request.cacheControl();
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl responseCaching = cacheResponse.cacheControl();
      if (responseCaching.immutable()) {
        return new CacheStrategy(null, cacheResponse);
      }

      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\"");
        }
        return new CacheStrategy(null, builder.build());
      }

      // 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);
    }
复制代码

又是长长的一大段,不怕,来咱们慢慢看看这个方法中缓存策略的生成过程,首先来看

if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }
复制代码

若是没有缓存response对象存在,就建立一个请求网络的缓存策略,OK,继续

// Drop the cached response if it's missing a required handshake. if (request.isHttps() && cacheResponse.handshake() == null) { return new CacheStrategy(request, null); } 复制代码

若是是HTTPS请求,而且缓存response对象的握手数据为null,就建立一个请求网络的缓存策略返回,继续

if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }
复制代码

若是是不可缓存的,就建立一个请求网络的缓存策略返回,let's继续

CacheControl requestCaching = request.cacheControl();
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

复制代码

判断请求中的缓存控制信息是否容许缓存或者查看是否知足指定条件,若是知足了指定的条件,就建立请求网络的缓存策略并返回,go on

CacheControl responseCaching = cacheResponse.cacheControl();
      if (responseCaching.immutable()) {
        return new CacheStrategy(null, cacheResponse);
      }
复制代码

若是响应的response中的缓存控制值是不可变的,说明数据稳定,建立一个使用缓存的缓存策略返回便可,继续 下面的代码不是很明白,不过不影响以前代码的理解,先跳过,回头有时间再细细研究这段代码。 回到CacheInterceptor的intercept方法中来,获得了缓存策略对象strategy,而后下面代码

if (cache != null) {
      cache.trackResponse(strategy);
    }
复制代码

这步操做是啥操做呢,咱们看下实现

synchronized void trackResponse(CacheStrategy cacheStrategy) {
    requestCount++;

    if (cacheStrategy.networkRequest != null) {
      // If this is a conditional request, we'll increment hitCount if/when it hits. networkCount++; } else if (cacheStrategy.cacheResponse != null) { // This response uses the cache and not the network. That's a cache hit.
      hitCount++;
    }
  }
复制代码

原来是请求次数计算和缓存命中次数的计算,继续往下看

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

若是从缓存中获得的数据不为null,而且通过缓存策略的计算,缓存策略中的response对象为null,就说明这个缓存不可用,就将其关闭。now go on

// If we're forbidden from using the network and the cache is insufficient, fail. 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(); } 复制代码

若是不能使用网络请求也不能使用缓存,那么久返回一个504的错误。

// If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
复制代码

若是不能使用网络,咱们就将缓存策略获得的response进行封装并返回。 下面就是从网络中获取数据了,调用了拦截器链的proceed方法,这个咱们先跳过,继续往下看

// If we have a cache response too, then we're doing a conditional get. 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中的状态码为304,那么就将从缓存中获取的response进行封装并返回。 最后

if (cache != null) {
      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.
        }
      }
    }
复制代码

首先判断响应是否有响应体,而且是不是可缓存的,若是可缓存,就进行缓存,而后对请求的方法进行判断,若是是无效的,就将数据中缓存中移除。 分析到这里,就将缓存拦截器分析完成了,下面咱们继续看链接拦截器。 #####OkHttp拦截器之ConnectInterceptor解析 按照惯例,咱们依旧是要看拦截器的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");
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
复制代码

惊了个呆,这个intercept方法这么短吗?是的,就是这么短,可是,短不见得就是简单,嗯哼,咱们来分析下这个方法中的代码。 首先获取真正的拦截器链,而后经过拦截器链获取request请求对象,而后获取拦截器中的StreamAllocation对象,咱们知道,这个对象是在前面就已经建立好了的,前面已经分析过,这里就再也不讲啦,好的,继续,咱们看到streamAllocation对象建立了一个叫作HttpCodec的对象实例,这个是什么东东呢,其实就是对咱们的request对象编码和对response对象进行解码操做的类,而后streamAllocation建立了一个RealConnection对象,而后将这些新建立的对象传入到了下一个拦截器中。

这里写图片描述
这样就完了???是的,真的就完了,但是咱们知道,链接服务器的操做都是在这个拦截器中执行的,那么具体在哪里呢?咱们下面来仔细分析下,真正的链接操做是在streamAllocation对象的newStream方法中,咱们进入这个方法中看下,

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

    try {
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }
复制代码

咱们来看关键代码,链接操做是在try代码块中,是findHealthyConnection方法,继续跟进,

private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
      boolean doExtensiveHealthChecks) throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          pingIntervalMillis, 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; } } 复制代码

看关键代码,就是while循环的第一行代码,仍是要跟进,城市套路深,我要回农村····

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
    boolean foundPooledConnection = false;
    RealConnection result = null;
    Route selectedRoute = null;
    Connection releasedConnection;
    Socket toClose;
    synchronized (connectionPool) {
      ···//忽略部分代码
      // Attempt to use an already-allocated connection. We need to be careful here because our
      // already-allocated connection may have been restricted from creating new streams.
      releasedConnection = this.connection;
      toClose = releaseIfNoNewStreams();
      if (this.connection != null) {//先找是否有可用且健康的链接,若是有,咱们就进行复用
        // We had an already-allocated connection and it's good. result = this.connection; releasedConnection = null; } if (!reportedAcquired) { // If the connection was never reported acquired, don't report it as released!
        releasedConnection = null;
      }

      if (result == null) {//这里说明没有可用的链接,那么咱们就要从链接池中获取了
        // Attempt to get a connection from the pool.
        Internal.instance.get(connectionPool, address, this, null);//从链接池中获取
        if (connection != null) {
          foundPooledConnection = true;
          result = connection;
        } else {
          selectedRoute = route;
        }
      }
    }
    ····//忽略部分代码
    //在这里进行真正的链接操做
    // Do TCP + TLS handshakes. This is a blocking operation.
    result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      reportedAcquired = true;

      // Pool the connection.
      Internal.instance.put(connectionPool, result);

      // If another multiplexed connection to the same address was created concurrently, then
      // release this connection and acquire that one.
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);

    eventListener.connectionAcquired(call, result);
    return result;
  }
复制代码

从上面的代码块的注释中能够看到,真正的链接是执行connect方法,咱们再进一层看看connect是如何实现的

public void connect(int connectTimeout, int readTimeout, int writeTimeout,
      int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
      EventListener eventListener) {
      //判断是否已经链接
    if (protocol != null) throw new IllegalStateException("already connected");

    RouteException routeException = null;
    List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs();
    ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
    ····//忽略部分代码
    while (true) {
      try {//关键代码在这里,判断是否须要进行隧道链接,若是须要就进行隧道链接
        if (route.requiresTunnel()) {
          connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
          if (rawSocket == null) {
            // We were unable to connect the tunnel but properly closed down our resources.
            break;
          }
        } else {
        //这里进行socket链接
          connectSocket(connectTimeout, readTimeout, call, eventListener);
        }
        establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
        eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);
        break;
      } catch (IOException e) {
         ····//忽略部分代码
      }
    }

     ····//忽略部分代码
  }
复制代码

好了,看完这个方法,咱们就知道具体是在哪里进行链接操做的了,到此为止呢,咱们也算是分析完了ConnectInterceptor拦截器

#####OkHttp拦截器之CallServerInterceptor解析 国际惯例,咱们先贴出intercept方法的代码

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

    long sentRequestMillis = System.currentTimeMillis();

    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);//在这里经过httpcodec向HTTP的流里写入request的header信息
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100 // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
      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();
      }
    }
	//到这里,咱们就完成了request的请求工做
    httpCodec.finishRequest();

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

    int 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
      responseBuilder = httpCodec.readResponseHeaders(false);

      response = responseBuilder
              .request(request)
              .handshake(streamAllocation.connection().handshake())
              .sentRequestAtMillis(sentRequestMillis)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();

      code = response.code();
    }

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

    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 = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      streamAllocation.noNewStreams();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }
复制代码

好了,CallServerInterceptor拦截器的分析到此结束, 整个okhttp的源码分析也结束了,以上只是我的的阅读源码的理解,之后还会常常阅读的,逐渐提高逐渐阅读源码的能力,若是有问题欢迎你们的指正。

相关文章
相关标签/搜索