OkHttp学习(二)-请求流程分析

整体流程

下面这张图很好的说明了OkHttp的网络请求流程,来自于拆轮子系列:拆 OkHttp

OkHttp最简单的使用如下所示:

  OkHttpClient client = new OkHttpClient.Builder().build();
  Request request = new Request.Builder().url("....").build();
  Call call = client.newCall(request);
  call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {

    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {

    }
  });

1. OkHttpClient.Builder

public static final class Builder {
    public Builder() {
        dispatcher = new Dispatcher();
        protocols = DEFAULT_PROTOCOLS;//HTTP_2, Http_1_1
        connectionSpecs = DEFAULT_CONNECTION_SPECS; //MODERN_TLS, CLEARTEXT
        eventListenerFactory = EventListener.factory(EventListener.NONE);
        proxySelector = ProxySelector.getDefault();
        cookieJar = CookieJar.NO_COOKIES;
        socketFactory = SocketFactory.getDefault();
        hostnameVerifier = OkHostnameVerifier.INSTANCE;
        certificatePinner = CertificatePinner.DEFAULT;
        proxyAuthenticator = Authenticator.NONE;
        authenticator = Authenticator.NONE;
        connectionPool = new ConnectionPool();
        dns = Dns.SYSTEM;
        followSslRedirects = true;
        followRedirects = true;
        retryOnConnectionFailure = true;
        connectTimeout = 10_000;
        readTimeout = 10_000;
        writeTimeout = 10_000;
        pingInterval = 0;
    }
}
  1. 默认Cache是为空
  2. 默认CookieJar = CookieJar.NO_COOKIES, 即不做cookie管理
  3. 创建一个ConnectionPool(maxIdleConnections: 5, keepAliveDurations: 5, TimeUnit.Minutes)

2. Request.Builder

public static class Builder {
        HttpUrl url;
        String method;
        Headers.Builder headers;
        RequestBody body;
        Object tag;

        //默认请求方法是GET, 默认Headers是空的,后面会再添加
        public Builder() {
            this.method = "GET";
            this.headers = new Headers.Builder();
        }

        //根据传入的url生成一个HttpUrl对象
        public Builder url(String url) {
            if (url == null)
              throw new NullPointerException("url == null");

            //自动将web socket url替换为http的url
            if (url.regionMatches(true, 0, "ws:", 0, 3)) {
                url = "http:" + url.substring(3);
            } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
                url = "https:" + url.substring(4);
            }

            HttpUrl parsed = HttpUrl.parse(url);
            if (parsed == null)
                throw new IllegalArgumentException("unexpected url: " + url);
            return url(parsed);
        }
        ....
}

  1. 默认Request的body是null
  2. headers也为空

3. OkHttpClient.newCall(Request request)

@Override
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

可以看到,内部实际调用的是RealCall.newCall来生成Call

3.1 RealCall.newRealCall

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    //默认client.eventListenerFactory返回OkHttpClient.Builder.eventListenerFactory
    //Builder.eventListenerFactory是默认的EventListener.NONE即一个空实现
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}

3.2 new RealCall

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

RealCall内部会创建RetryAndFollowUpInterceptor

4. RealCall.enqueue

@Override
public void enqueue(Callback responseCallback) {
    synchronized (this) {
        if (executed)
            throw new IllegalStateException("Already Executed");
        executed = true;
    }
    // 获取response.body().close()方法执行时的异常stack trace,将获取到的
    // stack trace信息赋予retryAndFollowUpInterceptor
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

RealCall.enqueue内部手机调用的Dispatcher.enqueue, 在调用Dispatcher.enqueue之前会创建一个AsyncCall, AsyncCall实际就是一个Runnable

4.1 new AsyncCall

//NamedRunnable继承自Runnable, 实质是在run()方法调用子类的execute()方法,这里即AsyncCall的execute()方法
final class AsyncCall extends NamedRunnable {
  AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
  }
  ...
}

NamedRunnable继承自Runnable, 实质是在run()方法调用子类的execute()方法,这里即AsyncCall的execute()方法

4.2 Dispatcher.enqueue

/*
* 如果一个host的同时请求数量没有达到上限的话,将传入的AsyncCall放入running队列中,并将AsyncCall放入线程池中执行该任务(即调用run()方法,
* 而AsyncCall继承自NamedRunnable, 所以最终会调用execute方法)
* 如果达到上限,则将传入的AsyncCall放入ready队列中
*/
synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
        runningAsyncCalls.add(call);
        executorService().execute(call);
    } else {
        readyAsyncCalls.add(call);
    }
}

如果一个host即服务器的同时请求数量没有达到上限的话,会把新建的任务放入正在运行的队列中,同时在线程池中执行该任务,如果请求数量达到上限,则先将任务放入代运行队列中

4.3 AsyncCall.execute


  @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 {
               responseCallback.onFailure(RealCall.this, e);
          }
      } finally {
          //网络请求结束,将当前AsyncCall从running队列中删除
          //检查ready队列中是否有待处理的请求,有则处理
          client.dispatcher().finished(this);
      }
  }

线程池执行以后会调用Runnable.run方法,同时通过之前4.1可以知道AsyncCall继承自NamedRunnable,最终会调用execute方法

4.4 RealCall.getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List<Interceptor> interceptors = new ArrayList<>();
        //如果有自定义的Interceptor, 先添加全部的自定义Interceptor
        interceptors.addAll(client.interceptors());
        //添加RetryAndFollowUpInterceptor
        interceptors.add(retryAndFollowUpInterceptor);
        //client默认的cookieJar是一个空实现既不做cookie管理, 添加BridgeInterceptor
        interceptors.add(new BridgeInterceptor(client.cookieJar()));
        //client默认internalCache = null, 添加CacheInterceptor
        interceptors.add(new CacheInterceptor(client.internalCache()));
        //添加ConnectInterceptor
        interceptors.add(new ConnectInterceptor(client));
        //一般情况下forWebSocket = false, 添加client的networkInterceptors
        if (!forWebSocket) {
            interceptors.addAll(client.networkInterceptors());
        }
        //添加CallServerInterceptor
        interceptors.add(new CallServerInterceptor(forWebSocket));

        //创建RealInterceptorChain,并调用chain.proceed()
        //eventListener是一个空实现
        Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
                originalRequest, this, eventListener, client.readTimeoutMillis());
        return chain.proceed(originalRequest);
    }

依次添加各个Interceptor添加的顺序是:

  1. 用户自定义的Interceptor
  2. 之前创建的RetryAndFollowUpInterceptor
  3. 添加一个新建的BridgeInterceptor
  4. 添加一个新建的CacheInterceptor
  5. 添加一个新建的ConnectInterceptor
  6. 如果不是针对WebSocket, 则再添加自定义的network interceptor
  7. 最后添加新建的CallServerInterceptor

4.5 RealInterceptorChain.proceed

@Override
public Response proceed(Request request) throws IOException {
    //streamAllocation = null, httpCodec = null, connection = null
    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++;

      if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
          throw new IllegalStateException(...);
      }
      if (this.httpCodec != null && calls > 1) {
          throw new IllegalStateException(...);
      }

      // 依次调用各Interceptor
      RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
              connection, index + 1, request, call, eventListener, readTimeout);
      Interceptor interceptor = interceptors.get(index);
      Response response = interceptor.intercept(next);

      // 确保每个Interceptor都只执行一次proceed
      if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
          throw new IllegalStateException(...);
      }

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

      if (response.body() == null) {
          throw new IllegalStateException(...);
      }

      return response;
  }

该方法最重要的一点是依次调用各个Interceptorintercept()方法, 调用的顺序为:

  1. 自定义Interceptors
  2. RetryAndFollowUpInterceptor
  3. BridgeInterceptor
  4. CacheInterceptor
  5. ConnectInterceptor
  6. 自定义添加的Network Interceptors
  7. CallServerInterceptor

4.6 RetryAndFollowUpInterceptor.interceptor

Request request = chain.request();
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        //RealCall
        Call call = realChain.call();
        //EventListener的空实现
        EventListener eventListener = realChain.eventListener();

        //新建一个StreamAllocation, 根据OkHttpClient的SSLSocketFactory, HostnameVerifier, Dns, CertificatePinner
        //等属性新建一个Address对象
        //callStackTrace是之前调用RealCall.enqueue时设置的关于Response.body().close()方法的异常栈
        streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
                call, eventListener, callStackTrace);

        int followUpCount = 0;
        Response priorResponse = null;
        while (true) {
            if (canceled) {
                streamAllocation.release();
                throw new IOException("Canceled");
            }

            Response response = null;
            boolean releaseConnection = true;
            try {
                //调用下一个Interceptor, 即BridgeInterceptor的intercept方法
                //NOTE: 要执行完所有Interceptor.intercept以后再去执行之后的代码逻辑
                response = realChain.proceed(request, streamAllocation, null, null);
                releaseConnection = false;
            } catch (RouteException e) {
                if (!recover(e.getLastConnectException(), false, request)) {
                    throw e.getLastConnectException();
                }
                releaseConnection = false;
                continue;
            } catch (IOException e) {
                boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
                if (!recover(e, requestSendStarted, request)) throw e;
                releaseConnection = false;
                continue;
            } finally {
              ....
            }
            ...
        }
    }

NOTE: 所有Interceptor是一个完整的调用链,RetryAndFollowUpInterceptor.intercept是起始处,当所有的Interceptor执行完以后,RetryAndFollowUpInterceptor才会再执行finally以后的代码逻辑

4.7 BridgeInterceptor.interceptor

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

        //执行CacheInterceptor.intercept
        Response networkResponse = chain.proceed(requestBuilder.build());

        ....
}

BridgeInterceptor.intercept主要就是根据Request添加一些必要的http请求的header

4.8 CacheInterceptor.interceptor

    @Override
    public Response intercept(Chain chain) throws IOException {
        //默认情况下, cache = null
        Response cacheCandidate = cache != null
                ? cache.get(chain.request())
                : null;

        long now = System.currentTimeMillis();

        //一帮情况下,由于cacheCandidate = null, 所以直接返回一个new CacheStrategy(request, null)
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;

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

        //如果cacheResponse = null, 则调用cacheCandidate的ResponseBody.close()
        if (cacheCandidate != null && cacheResponse == null) {
            closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
        }

        //如果既没有Request也没有Cache Response, 生成一个空body, code = 504的Response
        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();
        }

        // 如果Request = null, 但是有缓存的Response, 返回缓存的Response
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                    .cacheResponse(stripBody(cacheResponse))
                    .build();
        }

        //调用ConnectInterceptor.interceptor()
        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());
            }
        }

        ...

}

4.9 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");
        //创建RealConnection和HttpCodec
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
        //调用CallServerInterceptor.interceptor
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }

4.9.1 StreamAllocation.newStream

  public HttpCodec newStream(
            OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {

        //10000
        int connectTimeout = client.connectTimeoutMillis();
        //10000
        int readTimeout = chain.readTimeoutMillis();
        //10000
        int writeTimeout = client.writeTimeoutMillis();
        //true
        boolean connectionRetryEnabled = client.retryOnConnectionFailure();

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

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

4.9.2 StreamAllocation.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.9.3 StreamAllocation.findConnection


private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          boolean connectionRetryEnabled) throws IOException {
        Route selectedRoute;
        synchronized (connectionPool) {
            if (released)
              throw new IllegalStateException("released");
            if (codec != null)
              throw new IllegalStateException("codec != null");
            if (canceled)
              throw new IOException("Canceled");

            // 如果this.connection已经创建,则直接返回this.connection
            // 但第一次创建时,this.connection = null
            RealConnection allocatedConnection = this.connection;
            if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
                return allocatedConnection;
            }

            //从connectionPool中尝试获取,注意route = null
            Internal.instance.get(connectionPool, address, this, null);
            if (connection != null) {
                return connection;
            }

            selectedRoute = route;
        }

        // If we need a route, make one. This is a blocking operation.
        if (selectedRoute == null) {
            selectedRoute = routeSelector.next();
        }

        RealConnection result;
        synchronized (connectionPool) {
            if (canceled) throw new IOException("Canceled");

            // Now that we have an IP address, make another attempt at getting a connection from the pool.
            // This could match due to connection coalescing.
            Internal.instance.get(connectionPool, address, this, selectedRoute);
            if (connection != null) {
                route = selectedRoute;
                return connection;
            }

            // Create a connection and assign it to this allocation immediately. This makes it possible
            // for an asynchronous cancel() to interrupt the handshake we're about to do.
            route = selectedRoute;
            refusedStreamCount = 0;
            result = new RealConnection(connectionPool, selectedRoute);
            acquire(result);
        }

        // Do TCP + TLS handshakes. This is a blocking operation.
        result.connect(
                connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
        routeDatabase().connected(result.route());

        Socket socket = null;
        synchronized (connectionPool) {
            // 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);

        return result;
    }

4.10 CallServerInterceptor.intercept

    @Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        //HttpCodec是在ConnectInterceptor中创建的
        //如果是https协议则是Http2Codec, 否则是Http1Codec
        HttpCodec httpCodec = realChain.httpStream();
        StreamAllocation streamAllocation = realChain.streamAllocation();
        //RealConnection也是在ConnectInterceptor中创建的
        RealConnection connection = (RealConnection) realChain.connection();
        Request request = realChain.request();

        long sentRequestMillis = System.currentTimeMillis();
        //向已经建立好的连接写入请求头部, 如果是https,还会创建Http2Stream对象
        httpCodec.writeRequestHeaders(request);

        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {

            //如果请求头部中有Except: 100-continue, 在发送body之前先发送一个请求询问server是否愿意接收数据,只有当server
            //返回http/1.1 100 Continue时才继续发送数据,如果服务器没有返回这个,则直接返回server返回的code
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                httpCodec.flushRequest();
                responseBuilder = httpCodec.readResponseHeaders(true);
            }

            if (responseBuilder == null) {
                // Write the request body if the "Expect: 100-continue" expectation was met.
                // 写入Body
                Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
                BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
                request.body().writeTo(bufferedRequestBody);
                bufferedRequestBody.close();
            } 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();

        //读取Response的头部
        if (responseBuilder == null) {
            responseBuilder = httpCodec.readResponseHeaders(false);
        }

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

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

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

        return response;
    }


4.11 ConnectIntercptor.intercept

直接向上返回了CallServerInterceptor得到的Response

4.12 CacheInterceptor.intercept

    @Override
    public Response intercept(Chain chain) throws IOException {

        ...

        //调用ConnectInterceptor.interceptor()
        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)) {
                //将response放入缓存
                CacheRequest cacheRequest = cache.put(response);
                return cacheWritingResponse(cacheRequest, response);
            }
            //如果method是post, put, patch, delete, move,则移除缓存
            if (HttpMethod.invalidatesCache(networkRequest.method())) {
                try {
                    cache.remove(networkRequest);
                } catch (IOException ignored) {
                    // The cache cannot be written.
                }
            }
        }

        return response;
    }

4.13 BridgeInterceptor.intercept

@Override
public Response intercept(Chain chain) throws IOException {

    ...
    Response networkResponse = chain.proceed(requestBuilder.build());
    //处理Cookie
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
                .request(userRequest);

    //如果含有Content-Encoding:gzip,即内容是以gzip压缩过的,则解压缩,并移除
    //头部中的Content-Encoding和Content-Length
    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);
        responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
}

4.14 RetryAndFollowUpInterceptor.intercept


    @Override
    public Response intercept(Chain chain) throws IOException {

        ....
        // 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);

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

            closeQuietly(response.body());
            //最多重试20次
            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());
            }
            //检查新请求的服务器地址与老地址是否相同,不相同则回收旧的StramAllocation的内存,如果相同
            //则复用之前的Connection
            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 = followUp;
            priorResponse = response;
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 简介 目前在HTTP协议请求库中,OKHttp应当是非常火的,使用也非常的简单。网上有很多文章写了关于OkHttp...
    第八区阅读 1,371评论 1 5
  • OkHttp解析系列 OkHttp解析(一)从用法看清原理OkHttp解析(二)网络连接OkHttp解析(三)关于...
    Hohohong阅读 20,965评论 4 58
  • 这篇文章主要讲 Android 网络请求时所使用到的各个请求库的关系,以及 OkHttp3 的介绍。(如理解有误,...
    小庄bb阅读 1,145评论 0 4
  • 关于okhttp是一款优秀的网络请求框架,关于它的源码分析文章有很多,这里分享我在学习过程中读到的感觉比较好的文章...
    蕉下孤客阅读 3,594评论 2 38
  • 《红楼梦》里贾宝玉有一句流传很广的话: 女孩儿未出嫁,是无价之宝珠;出了嫁,不知怎么就变出许多不好...
    森鱼阅读 1,138评论 0 2