OkHttp3初探:拦截器源码分析

目录

一、OkHttp3的拦截器

二、Interceptor接口

三、RetryAndFollowUpInterceptor

三、BridgeInterceptor

四、CacheInterceptor

五、ConnectInterceptor

六、CallServerInterceptor

七、addInterceptor与addNetworkInterceptor的区别

八、主要参考


 


一、OkHttp3的拦截器

在OkHttp3中,在执行RealCall的execute方法时,在通过HttpClient的Dispatcher执行execute方法后,将当前RealCall添加到执行/等待队列中,然后调用getResponseWithInterceptorChain()方法返回的Response;

在getResponseWithInterceptorChain()方法中,在真正执行网络请求前,会先添加上一串拦截器Interceptor,如下所示:

/** [email protected] */
/** 执行网络请求,并返回服务器响应Response */
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 拦截器容器
    List interceptors = new ArrayList<>();
    // 1.先添加用户自定义的拦截器
    interceptors.addAll(client.interceptors());
  	// 2.重试与重定向拦截器,如重连接,对3xx系列重连接做出响应
    interceptors.add(new RetryAndFollowUpInterceptor(client));
  	// 3.桥接拦截器,切换对用户友好的Request/Response与对服务器友好的Request/Response;
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
  	// 4.缓存拦截器,用来处理网络缓存;
    interceptors.add(new CacheInterceptor(client.internalCache()));
    // 5.IO连接拦截器,真实IO操作;
    interceptors.add(new ConnectInterceptor(client));
  
    
    if (!forWebSocket) {
      // 6.如果不是WebSocket长链接,添加用户自定义的网络拦截器
      interceptors.addAll(client.networkInterceptors());
    }
  	// 7.服务器连接拦截器,连接服务器;
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 创建拦截器链的实例
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null,0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      // 开始递归调用不同拦截器,并得到服务器返回结果
      Response response = chain.proceed(originalRequest);
      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);
      }
    }
  }

二、Interceptor接口

/**  Interceptor.class interface*/
/**
 * 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 {
  
  /** 入参为拦截器链,返回值为Http响应Response */
  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都要实现Interceptor接口;其中intercept()方法作为拦截器的拦截方法,执行具体逻辑,proceed()方法作为递归调用下一个拦截器的方法,从而保证拦截器链按顺序执行下去;

三、RetryAndFollowUpInterceptor

重连接与重定向拦截器:在intercept()方法中先做好网络连接前的准备,如果网络请求没有被取消掉,就开始递归调用proceed方法,获取下一层Interceptor的返回值,赋值给Response对象,在递归的过程中可能会出现异常,比如服务器返回3XX类型的重定向Reponse,则根据Response携带的信息,在followUpRequest方法中创建新的Request对象,这里命名为followUp。默认最多可重定向20次,超过20次则抛出异常;如果出现IO类连接关闭异常,则会重试;

/** intercept@RetryAndFollowUpInterceptor */
/** 拦截方法 */
@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Transmitter transmitter = realChain.transmitter();
    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
        transmitter.prepareToConnect(request);
        if (transmitter.isCanceled()) {
            throw new IOException("Canceled");
        }
        Response response;
        boolean success = false;
        try {
        	// 递归调用proceed方法,获取下一层Interceptor的返回结果
            response = realChain.proceed(request, transmitter, null);
            success = true;
        } catch (RouteException e) {
            // 若不满足重定向的条件,抛出异常
            if (!recover(e.getLastConnectException(), transmitter, false, request)) {
                throw e.getFirstConnectException();
            }
            // 重试
            continue;
        } catch (IOException e) {
            boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
            // 根据异常类型,如果不满足重试条件,则抛出异常
            if (!recover(e, transmitter, requestSendStarted, request)) throw e;
            // 重试
            continue;
        } finally {
            if (!success) {
            	// 若抛出了异常,释放资源
                transmitter.exchangeDoneDueToException();
            }
        }
        // 在本次response中设置上一次的response,但将body设置为null
        if (priorResponse != null) {
            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
        Request followUp = followUpRequest(response, route);
        if (followUp == null) {
        	// 没有用于重定向的Request了,停止timeout计时并返回response
            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();
        }
        // 重定向不超过MAX_FOLLOW_UPS,默认20次,否则抛出异常
        if (++followUpCount > MAX_FOLLOW_UPS) {
            throw new ProtocolException("Too many follow-up requests: " + followUpCount);
        }
        // 修改下次重定向的request
        request = followUp;
        // 记录上一次请求的response
        priorResponse = response;
    }
}

三、BridgeInterceptor

桥梁拦截器,其主要作用是对Requset与Response的Header做一次换装,分别将对用户友好的Request转换成对服务器友好的Request,将对服务器友好的Response转换成对用户友好的Response,也就是二次封装。主要封装的内容为HTTP中的Header属性,比如MediaType,ContentLength等,如果Cookie不为空,也会把Cookie封装到新的Request中;另外默认Accept-Encoding为gzip。

/** intercept@BridgeInterceptor */
/** 拦截方法 */
@Override
public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();
    RequestBody body = userRequest.body();
    // 将userRequest中原有的属性复制到新的requestBuilder中;
    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");
        }
    }
    // 重新封装Header相关属性
    if (userRequest.header("Host") == null) {
        requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    if (userRequest.header("Connection") == null) {
        requestBuilder.header("Connection", "Keep-Alive");
    }
    boolean transparentGzip = false;
    // 若未设置Accept-Encoding,自动设置gzip
    if (userRequest.header("Accept-Encoding") == null 
        && userRequest.header("Range") == null) {
        transparentGzip = true;
        requestBuilder.header("Accept-Encoding", "gzip");
    }
    // 将userRequest中的cookies设置进builder
    List 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());
    }
    // 递归调用proceed,得到服务器返回值
    Response networkResponse = chain.proceed(requestBuilder.build());
    // 对响应的header进行处理
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
   	// 根据服务端的响应构建新的Response,并将userRequest设置为其request
   	Response.Builder responseBuilder = networkResponse.newBuilder()
            .request(userRequest);
    // 若之前设置了gzip压缩且response中也包含了gzip压缩,则进行gzip解压
    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();
}

四、CacheInterceptor

缓存拦截器:负责对缓存的读取与更新;根据当前时间,请求Request,缓存信息三个属性来构建缓存策略,通过缓存策略构建新的网络请求networkRequest,根据networkRequest的状态分成不同的情况处理:

若当前networkRequest为空,则代表当前请求不需要网络连接,继续通过缓存策略获取缓存响应cacheResponse,若cacheResponse为空,则代表没有缓存,直接构建访问失败的响应返回;若cacheResponse不为空,则直接返回cacheResponse;

若当前networkRequest不为空,则代表当前请求需要网络连接,通过调用chain.proceed()方法递归地去获取下一层拦截器的返回值;在后续的拦截器中会真正执行网络请求;

当获取到下一层拦截器的响应后,根据相应的策略判断是否需要缓存数据,重新封装本拦截器的响应Response;最终返回CacheInterceptor的Response;

/** intercept@CacheInterceptor */
/** 拦截方法 */
@Override
public Response intercept(Chain chain) throws IOException {
	// 尝试获取缓存的cache
    Response cacheCandidate = cache != null
            ? cache.get(chain.request())
            : null;
    long now = System.currentTimeMillis();
    // 构建缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(now, 
                               chain.request(),                 
                               cacheCandidate)
                               .get();
    // 通过缓存策略获取新的request
    Request networkRequest = strategy.networkRequest;
    // 通过缓存策略获取缓存中取出的response
    Response cacheResponse = strategy.cacheResponse;
    if (cache != null) {
        cache.trackResponse(strategy);
    }
    if (cacheCandidate != null && cacheResponse == null) {
        closeQuietly(cacheCandidate.body()); 
    }
    // 若无需网络请求,并且没有缓存,构建一个请求失败的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();
    }
    // 若无需网络请求,则直接返回
    if (networkRequest == null) {
        return cacheResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .build();
    }
    Response networkResponse = null;
    try {
    	// 需要网络请求,递归调用proceed方法获取下一个拦截器的response
        networkResponse = chain.proceed(networkRequest);
    } finally {
        // 如果出现IO或者其他crash,回收缓存body的资源
        if (networkResponse == null && cacheCandidate != null) {
            closeQuietly(cacheCandidate.body());
        }
    }

    // 如果缓存不为空,且返回状态码为304,则整合Header属性并返回,并更新缓存中的内容
    if (cacheResponse != null) {
        if (networkResponse.code() == HTTP_NOT_MODIFIED) {	// 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();
            cache.trackConditionalCacheHit();
            cache.update(cacheResponse, response);
            return response;
        } else {
            closeQuietly(cacheResponse.body());
        }
    }
    // 构建response
    Response response = networkResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
    // 缓存相应的请求Request与响应的Response信息
    if (cache != null) {
        if (HttpHeaders.hasBody(response) 
             && CacheStrategy.isCacheable(response, networkRequest)) {
            // 缓存
            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;
}

五、ConnectInterceptor

连接拦截器:实现与服务器的连接,在内部的intercept方法中对GET方法做特殊处理,从而更新Exchange这个真正执行IO操作的实现类。

/** intercept@ConnectInterceptor */
/** 拦截方法 */
@Override 
public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    Transmitter transmitter = realChain.transmitter();

    // We need the network to satisfy this request. 
    // Possibly for validating a conditional GET.
    // 对GET请求做一些特殊处理
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);

    // 实际IO操作在exchange的connection方法中调用ExchangeCodeC.connection()进行
    return realChain.proceed(request, transmitter, exchange);
  }

六、CallServerInterceptor

连接服务拦截器:负责发起实际网络请求,将请求写入缓冲区,读取服务器的响应,然后封装响应,返回上一层拦截器;

/** 拦截器链的最后一个拦截器,发起实际网络请求,读取服务器响应 */
/** intercept@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();
    // 更新Http引擎的sentRequestMillis属性
    exchange.writeRequestHeaders(request);

    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // 如果需要服务器响应“100-continue”,根据响应做出相应处理
      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 {
          // 如果服务器响应“100-continue”,则调用Okio将请求实体写入缓冲区
          // 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()) {
          // 如果服务器没有响应“100-continue”,则不再写入缓冲区,避免http1请求重用;
          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 response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    int code = response.code();
    if (code == 100) {
      // 即使没发送请求服务器也响应“100-continue”,就重新读取一次真实Response
      response = exchange.readResponseHeaders(false)
          .request(request)
          .handshake(exchange.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();

      code = response.code();
    }

    // response的Header部分封装完成
    exchange.responseHeadersEnd(response);

    if (forWebSocket && code == 101) {
      // 连接正在升级,须保证拦截器获取到非空response.body
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      // response的Body部分封装完成
      response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build();
    }

    // 判断是否需要断开连接
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      exchange.noNewExchangesOnConnection();
    }

    // 对204,205状态时content-length大于零抛出异常
    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }

七、addInterceptor与addNetworkInterceptor的区别

addInterceptor应用拦截器,其加入时机是在RetryAndFollowUpInterceptor之前,,有如下特点:

  1. 不需要关心后续拦截器的响应;
  2. 只调用一次,不管Response是网络请求返回还是从缓存中直接读取的;
  3. 观察应用程序的初衷,不关心OkHttp注入的头信息;
  4. 允许提前终止网络调用,而不用被动递归调用chain.proceed();
  5. 同样也允许重试,可以多次调用chain.proceed();

addNetworkInterceptor,顾名思义,网络拦截器,是在ConnectInterceptor之后,CallServerInterceptor之前,同时还要判断是否是websocket连接,主要作用:

  1. 能够操作拦截器链中间过程的Response,如重试或者重定向;
  2. 如果没有发起网络连接,直接读取的缓存Response,则不会调用网络拦截器;
  3. 只关心网络上传输的数据;
  4. 因为在拦截器链中,所以可以读取到chain携带的Request,也可发起网络连接;

八、主要参考

参考链接(1):https://github.com/square/okhttp

参考链接(2):https://blog.csdn.net/qq_21556263/article/details/98847306

参考链接(3):https://github.com/AndroidPreView/AndroidNote/wiki/Okhttp-%E7%9A%84addInterceptor-%E5%92%8C-addNetworkInterceptor-%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F

 

 

你可能感兴趣的:(Android)