zoukankan      html  css  js  c++  java
  • Okhttp解析—Interceptor详解

    Okhttp解析—Interceptor详解

    Interceptor可以说是okhttp的精髓之一,Okhttp重写请求/响应、重试、缓存响应等操作,基本都是在各个Interceptor中完成的,上篇文章分析Okhttp运行流程时只是简单带过,那么这篇文章就来详细分析下Interceptor以及拦截器链机制。

    一、Interceptor以及InterceptorChain

    /**
     * Observes, modifies, and potentially short-circuits requests going out and the corresponding
     * responses coming back in. Typically interceptors add, remove, or transform headers on the request
     * or response.
     */
    public interface Interceptor {
      Response intercept(Chain chain) throws IOException;
    
      interface Chain {
        Request request();
    
        Response proceed(Request request) throws IOException;
    
        /**
         * Returns the connection the request will be executed on. This is only available in the chains
         * of network interceptors; for application interceptors this is always null.
         */
        @Nullable Connection connection();
    
        Call call();
    
        int connectTimeoutMillis();
    
        Chain withConnectTimeout(int timeout, TimeUnit unit);
    
        int readTimeoutMillis();
    
        Chain withReadTimeout(int timeout, TimeUnit unit);
    
        int writeTimeoutMillis();
    
        Chain withWriteTimeout(int timeout, TimeUnit unit);
      }
    }
    
    

    Interceptor最主要的就是其intercept()函数,InterceptorChain则是Interceptor一个内部类,它的主要作用就是链式有序调用Interceptor。

    Okhttp内置了5种Interceptor它们分别是RetryAndFollowUpInterceptor
    、BridgeInterceptor、CacheInterceptor、ConnectInterceptor
    、CallServerInterceptor。正是这5种Interceptor完成了重写、重试、缓存、请求等操作,接下来我们来逐一分析它们的作用。

    二、Interceptor链式调用

    拦截器调用的入口是在ReallCall的getResponseWithInterceptorChain()函数中。

    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(new RetryAndFollowUpInterceptor(client));
        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, transmitter, null, 0,
            originalRequest, this, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());//1
    
        boolean calledNoMoreExchanges = false;
        try {
          Response response = chain.proceed(originalRequest);//2
          if (transmitter.isCanceled()) {
            closeQuietly(response);
            throw new IOException("Canceled");
          }
          return response;
        } catch (IOException e) {
          calledNoMoreExchanges = true;
          throw transmitter.noMoreExchanges(e);
        } finally {
          if (!calledNoMoreExchanges) {
            transmitter.noMoreExchanges(null);
          }
        }
      }
    

    我们看到该函数一开始就构造了一个List然后向里边添加所有的Interceptor,包括我们自定义的Application Interceptor、系统预置的Interceptor、自定义的Network Interceptor等。
    然后在注释1处构造RealInterceptorChain实例并传入刚刚的interceptor list还有就是我们看到第四个参数此处传入的是0。该参数表示接下来要调用interceptor list中哪个interceptor。
    最后在注释2处调用chain.proceed()并传入原始请求。

    @Override public Response proceed(Request request) throws IOException {
      return proceed(request, transmitter, exchange);
    }
    
    public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
        throws IOException {
      if (index >= interceptors.size()) throw new AssertionError();
    
      calls++;
    
      // If we already have a stream, confirm that the incoming request will use it.
      if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
        throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
            + " must retain the same host and port");
      }
    
      // If we already have a stream, confirm that this is the only call to chain.proceed().
      if (this.exchange != null && calls > 1) {
        throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
            + " must call proceed() exactly once");
      }
    
      // Call the next interceptor in the chain.
      RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
          index + 1, request, call, connectTimeout, readTimeout, writeTimeout);//1
      Interceptor interceptor = interceptors.get(index);//2
      Response response = interceptor.intercept(next);//3
    
      // Confirm that the next interceptor made its required call to chain.proceed().
      if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
        throw new IllegalStateException("network interceptor " + interceptor
            + " must call proceed() exactly once");
      }
    
      // Confirm that the intercepted response isn't null.
      if (response == null) {
        throw new NullPointerException("interceptor " + interceptor + " returned null");
      }
    
      if (response.body() == null) {
        throw new IllegalStateException(
            "interceptor " + interceptor + " returned a response with no body");
      }
    
      return response;
    }
    

    proceed函数我们主要关注3个注释处的操作。
    在注释1处构造RealInterceptorChain,其中参数4此时变为index+1,index就是之前在getResponseWithInterceptorChain中构造RealInterceptorChain时传入的值。

    注释2处通过index从Interceptor list中取出对应的Interceptor。
    注释3处调用Interceptor的intercept()方法,参数传入注释1处构造的RealInterceptorChain。因为Interceptor的操作都是在intercept()函数中完成的,所以该操作完成了当前Interceptor的调用。同时在每个Interceptor的intercept()函数中都会调用next.proceed()这样就开启了下一个Interceptor调用,如此反复最终像链条一样依次调用Interceptor list中所有的Interceptor。

    三、详解各个Interceptor

    上边我们分析了Interceptor是按顺序调用的,这里的顺序指的是添加到Interceptor list中的先后。由getResponseWithInterceptorChain()方法可以调用的顺序依次是(未考虑添加自定义Interceptor的情况):RetryAndFollowUpInterceptor-->BridgeInterceptor-->CacheInterceptor-->ConnectInterceptor-->CallServerInterceptor。

    RetryAndFollowUpInterceptor

    RetryAndFollowUpInterceptor的作用主要是重试与重定向。
    来看下它的Interceptor方法。

    @Override public Response intercept(Chain chain) throws IOException {
      Request request = chain.request();//获取传入的chain的request 此处的request是next的request
      RealInterceptorChain realChain = (RealInterceptorChain) chain;
      Transmitter transmitter = realChain.transmitter();
    
      int followUpCount = 0;
      Response priorResponse = null;
      while (true) {//开启一个无限循环
        transmitter.prepareToConnect(request);
    
        if (transmitter.isCanceled()) {//如果此时请求被cancel抛出异常
          throw new IOException("Canceled");
        }
    
        Response response;
        boolean success = false;
        try {
          response = realChain.proceed(request, transmitter, null);//调用next的proceed方法,即调用下一个Interceptor
          success = true;
        } catch (RouteException e) {//如果通过某个route连接失败则尝试恢复。注意此时请求尚未发送出去
          // The attempt to connect via a route failed. The request will not have been sent.
          if (!recover(e.getLastConnectException(), transmitter, false, request)) {
            throw e.getFirstConnectException();
          }
          continue;
        } catch (IOException e) {//如果连接server失败则尝试恢复,注意此时请求已发送。
          // An attempt to communicate with a server failed. The request may have been sent.
          boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
          if (!recover(e, transmitter, requestSendStarted, request)) throw e;
          continue;
        } finally {
          // The network call threw an exception. Release any resources.
          if (!success) {//执行不成功关闭
            transmitter.exchangeDoneDueToException();
          }
        }
    
        // Attach the prior response if it exists. Such responses never have a body.
        if (priorResponse != null) {//priorResponse不为空表示之前发生过重定向,此时为本次的response设置priorResponse
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                      .body(null)
                      .build())
              .build();
        }
    
        Exchange exchange = Internal.instance.exchange(response);
        Route route = exchange != null ? exchange.connection().route() : null;
        Request followUp = followUpRequest(response, route);//生成重定向的请求
    
        if (followUp == null) {//followUp == null说明无需重定向,返回当前respone
          if (exchange != null && exchange.isDuplex()) {
            transmitter.timeoutEarlyExit();
          }
          return response;
        }
    
        RequestBody followUpBody = followUp.body();
        if (followUpBody != null && followUpBody.isOneShot()) {
          return response;
        }
    
        closeQuietly(response.body());
        if (transmitter.hasExchange()) {
          exchange.detachWithViolence();
        }
    
        if (++followUpCount > MAX_FOLLOW_UPS) {//判断是否超过最大重定向次数
          throw new ProtocolException("Too many follow-up requests: " + followUpCount);
        }
    
        request = followUp;
        priorResponse = response;
      }
    }
    
    

    来,梳理下整个流程:

    1. 开启一个无限循环,直至没有重定向或有异常抛出才会结束。
    2. 处理cancel
    3. 调用下一个Interceptor获取respone
    4. 如果步骤3发生异常,尝试恢复并重试请求
    5. 如果步骤3未发生异常为,priorResponse不为空(如果priorResponse表示之前发生过重定向),为当前respone设置priorResponse(注意priorResponse body是null)
    6. 调用followUpRequest根据返回的code生成用于重定向的请求
    7. 步骤6生成的请求为空表示无需重定向,返回当前respone,结束循环
    8. 步骤6生成的请求不为空表示需要重定向,此时重定向次数+1,然后判断是否超过最大重定向次数,未超过则跳转到步骤2开始下次循环。

    步骤3是拦截器能链式有序调用的关键。
    步骤4的判断是否能恢复是通过recover()函数,具体看注释

    /**
     * Report and attempt to recover from a failure to communicate with a server. Returns true if
     * {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
     * be recovered if the body is buffered or if the failure occurred before the request has been
     * sent.
     */
    private boolean recover(IOException e, Transmitter transmitter,
        boolean requestSendStarted, Request userRequest) {
      // The application layer has forbidden retries.
    //应用设置禁止重试 返回false
      if (!client.retryOnConnectionFailure()) return false;
    
      // We can't send the request body again.
    //request设置仅能执行一次 返回false
      if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;
    
      // This exception is fatal.
    //异常是致命的 返回false
      if (!isRecoverable(e, requestSendStarted)) return false;
    
      // No more routes to attempt.
    //没有更多的route可供尝试 返回fasle
      if (!transmitter.canRetry()) return false;
    
      // For failure recovery, use the same route selector with a new connection.
      return true;
    }
    

    可以看出恢复操作是有条件的,在某些条件下是不能恢复的。另外上面说的不可恢复致命异常有ProtocolException、InterruptedIOException、SSLHandshakeException、CertificateException
    、SSLPeerUnverifiedException。

    步骤6的followUpRequest可以说是重定向的核心了,看下followUpRequest函数

    private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
      if (userResponse == null) throw new IllegalStateException();
      int responseCode = userResponse.code();//获取当前respone的返回code
    
      final String method = userResponse.request().method();
      switch (responseCode) {//根据responseCode的值分情况处理
        case HTTP_PROXY_AUTH://407
          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://401
          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")) {//307、308 两种code不对 GET、HEAD 以外的请求重定向
            return null;
          }
          // fall-through
        case HTTP_MULT_CHOICE://300
        case HTTP_MOVED_PERM://301
        case HTTP_MOVED_TEMP://302
        case HTTP_SEE_OTHER://303 
    //以上这四种code是可以进行重定向的
          // Does the client allow redirects?
          if (!client.followRedirects()) return null;//客户端不允许重定向 返回null
    
          String location = userResponse.header("Location");//获取Location以确定重定向目标
          if (location == null) return null;//Response的Location为null 返回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);//是否带body重定向
            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.request().url(), url)) {
            requestBuilder.removeHeader("Authorization");
          }
    
          return requestBuilder.url(url).build();//返回构造的重定向request
    
        case HTTP_CLIENT_TIMEOUT://408 实际很少用到,一般需要重复发送一个相同的请求
          // 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;
          }
    
          RequestBody requestBody = userResponse.request().body();
          if (requestBody != null && requestBody.isOneShot()) {
            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://503
          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;
      }
    }
    

    followUpRequest主要是根据返回的code分情况处理,如果允许重定向则返回新构造的request否则返回null表示不允许重定向。

    BridgeInterceptor

    BridgeInterceptor名为桥接拦截器主要作用就是把我们传入的request“重写”为实际向server请求的request,收到respone后“重写”respone。来看下其intercept函数

    @Override public Response intercept(Chain chain) throws IOException {
      Request userRequest = chain.request();
      Request.Builder requestBuilder = userRequest.newBuilder();
    
      RequestBody body = userRequest.body();//获取请求body
      if (body != null) {//请求发送前“重写”headers
        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()) {//cookie不为空则添加cookie
        requestBuilder.header("Cookie", cookieHeader(cookies));
      }
    
      if (userRequest.header("User-Agent") == null) {//设置User-Agent
        requestBuilder.header("User-Agent", Version.userAgent());
      }
    
      Response networkResponse = chain.proceed(requestBuilder.build());//开启下一个拦截器的调用
    
      HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());//从这里开始是收到respone然后对其“重写”
    
      Response.Builder responseBuilder = networkResponse.newBuilder()
          .request(userRequest);
    
      if (transparentGzip
          && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
          && HttpHeaders.hasBody(networkResponse)) {//如果之前采用gzip进行压缩,那么需要对respone进行解压
        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();
    }
    
    

    BridgeInterceptor完成的工作可以分为3步:

    1. 请求发送前对request“重写”,重写后的request才是实际去用来请求的request。
    2. 调用chain.proceed开启下一个拦截器调用,并拿到respone
    3. 对返回的respone进行“重写”,我们拿到的respone就是重写后的

    对request和respone的“重写”基本都是针对其headers,比如发送请求前未设置Accept-EncodingOkhttp会为你设置,在有cookie的情况下为你添加cookie,在拿到respone后如果需要解压缩Okhttp会为你自动解压缩。

    CacheInterceptor

    CacheInterceptor是缓存拦截器,主要作用是定义Okhttp的缓存机制。

    @Override public Response intercept(Chain chain) throws IOException {
      Response cacheCandidate = cache != null
          ? cache.get(chain.request())
          : null;//若当前有cache则根据请求获取对应的缓存
    
      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.
      //从cache获取respone不使用网络
      if (networkRequest == null) {
        return cacheResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .build();
      }
    
      Response networkResponse = null;
      try {
        networkResponse = chain.proceed(networkRequest);//调用下一个拦截器,从网络获取respone
      } 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) {//从网络获取respone且缓存非空 如果返回码为304 则更新缓存然后返回
          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());
        }
      }
    
      //没有缓存可供使用,读取网络响应构造respone
      Response response = networkResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .networkResponse(stripBody(networkResponse))
          .build();
    
      if (cache != null) {//cache不为空 把respone缓存到cache
        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;
    }
    

    整个流程如下:

    1. 判断是否有Cache,有的话根据request去尝试获取缓存
    2. 构造缓存策略
    3. 步骤1获取的缓存respone不为空但是不可用,关闭连接
    4. 设置禁止从网络获取响应且缓存不可用那么返回504失败
    5. 从cache获取respone 不使用网络(步骤1-5的作用就是在有请求时先尝试从本地获取缓存如果失败才会去从网络获取否则返回缓存)
    6. 调用下一个拦截器,从网络获取respone
    7. 从网络获取respone且缓存非空 如果返回码为304 则更新缓存然后返回
    8. 没有缓存可供使用,读取网络响应构造respone
    9. cache不为空 把respone缓存到cache(步骤6-9作用就是从网络获取respone然后把获得的respone缓存到本地)

    CacheInterceptor中涉及两个类:Cache、CacheStrategy,这里先不展开分析,等分析Okhttp缓存机制时再做详细介绍。

    ConnectInterceptor

    ConnectInterceptor是连接拦截器,它的intercept函数非常简洁:

    @Override public Response intercept(Chain chain) throws IOException {
      RealInterceptorChain realChain = (RealInterceptorChain) chain;
      Request request = realChain.request();
      Transmitter transmitter = realChain.transmitter();//获取Transmitter
      // We need the network to satisfy this request. Possibly for validating a conditional GET.
      boolean doExtensiveHealthChecks = !request.method().equals("GET");
      Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);//构造Exchange
    
      return realChain.proceed(request, transmitter, exchange);//开启下一个拦截器调用并传入Transmitter、Exchange。
    }
    

    它首先获取Transmitter,然后通过Transmitter的newExchange方法创建一个Exchange,把它传到下一个拦截器。
    Transmitter是应用和网络之间的一个桥梁,通过transmitter.newExchange构造一个Exchange实例

    Exchange newExchange(Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
      synchronized (connectionPool) {
        if (noMoreExchanges) {
          throw new IllegalStateException("released");
        }
        if (exchange != null) {
          throw new IllegalStateException("cannot make a new request because the previous response "
              + "is still open: please call response.close()");
        }
      }
    
      ExchangeCodec codec = exchangeFinder.find(client, chain, doExtensiveHealthChecks);
      Exchange result = new Exchange(this, call, eventListener, exchangeFinder, codec);
    
      synchronized (connectionPool) {
        this.exchange = result;
        this.exchangeRequestDone = false;
        this.exchangeResponseDone = false;
        return result;
      }
    }
    

    newExchange主要做了两件事:调用ExchangeFinder.find获取一个ExchangeCodec、构造一个Exchange。
    ExchangeFinder就是负责连接的创建,把创建好的连接放入连接池,如果连接池中已经有该连接,就直接取出复用。而ExchangeCodec则是对HTTP请求和HTTP响应编码
    Exchange则是用来进行发送和接收HTTP request和respone。

    CallServerInterceptor

    CallServerInterceptor是拦截器链中最后一个拦截器,与服务器的交互如,发出请求和接收响应都是它完成的。

    @Override public Response intercept(Chain chain) throws IOException {
      RealInterceptorChain realChain = (RealInterceptorChain) chain;
      Exchange exchange = realChain.exchange();
      Request request = realChain.request();
    
      long sentRequestMillis = System.currentTimeMillis();
    
      exchange.writeRequestHeaders(request);//向服务端写请求
    
      boolean responseHeadersStarted = false;
      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"))) {
          exchange.flushRequest();
          responseHeadersStarted = true;
          exchange.responseHeadersStart();
          responseBuilder = exchange.readResponseHeaders(true);
        }
    
        if (responseBuilder == null) {
          if (request.body().isDuplex()) {
            // Prepare a duplex body so that the application can send a request body later.
            exchange.flushRequest();
            BufferedSink bufferedRequestBody = Okio.buffer(
                exchange.createRequestBody(request, true));
            request.body().writeTo(bufferedRequestBody);
          } else {
            // Write the request body if the "Expect: 100-continue" expectation was met.
            BufferedSink bufferedRequestBody = Okio.buffer(
                exchange.createRequestBody(request, false));
            request.body().writeTo(bufferedRequestBody);
            bufferedRequestBody.close();
          }
        } else {
          exchange.noRequestBody();
          if (!exchange.connection().isMultiplexed()) {
            // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
            // from being reused. Otherwise we're still obligated to transmit the request body to
            // leave the connection in a consistent state.
            exchange.noNewExchangesOnConnection();
          }
        }
      } else {
        exchange.noRequestBody();
      }
    
      if (request.body() == null || !request.body().isDuplex()) {
        exchange.finishRequest();
      }
    
      if (!responseHeadersStarted) {
        exchange.responseHeadersStart();//从服务端获取请求
      }
    
      if (responseBuilder == null) {
        responseBuilder = exchange.readResponseHeaders(false);
      }
    
      Response response = responseBuilder
          .request(request)
          .handshake(exchange.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
        response = exchange.readResponseHeaders(false)
            .request(request)
            .handshake(exchange.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
    
        code = response.code();
      }
    
      exchange.responseHeadersEnd(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.newBuilder()
            .body(exchange.openResponseBody(response))
            .build();
      }
    
      if ("close".equalsIgnoreCase(response.request().header("Connection"))
          || "close".equalsIgnoreCase(response.header("Connection"))) {
        exchange.noNewExchangesOnConnection();
      }
    
      if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
        throw new ProtocolException(
            "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
      }
    
      return response;
    }
    

    在ConnectInterceptor中我们已经与服务器建立了连接,获取了输入输出流,所以CallServerInterceptor的intercept(Chain)方法逻辑就是把请求发送到服务器,然后获取服务器的响应。

    发送请求

    通过Exchange的writeRequestHeaders(request)方法写入请求的header;如果请求的body不为空,通过okio写入请求的body。

    获取响应

    通过Exchange的readResponseHeaders(boolean)方法读取响应的header;通过Exchange的openResponseBody(Response)方法读取响应的body。

    可以看出发送请求个获取响应都是通过exchange来进行的。

    至此Okhttp的拦截器机制我们就分析完了,以上是Okhttp已经定义好的拦截器,在实际的使用中我们可以自定义拦截器来完成我们想要的功能。

  • 相关阅读:
    将文件导入到SQL server数据库表中的字段中
    查看端口是否启用
    JS去除字符串左右两端的空格
    css常见问题
    iframe之局部刷新
    iframe局部刷新的二种实现方法
    模式识别复习目录
    linux下文件内容查找 转
    LaTeX技巧10:LaTeX数学公式输入初级入门
    matlab中高维数组怎么做PCA?
  • 原文地址:https://www.cnblogs.com/Robin132929/p/12151889.html
Copyright © 2011-2022 走看看