Android之OkHttp框架(三)--Interceptor

本文介绍了Android中OkHttp的Interceptor,它是对请求和响应进行拦截和处理的关键组件。Interceptor实现了请求的重定向、缓存等功能,通过链式结构进行操作。文中详细解析了Interceptor的流程、接口以及如何实现重试和重定向功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前第二部分的时候留下一块东西没有解决,就是Interceptor部分,这两天大概看了一下,做一下大致的整理吧。

OkHttp框架里面对应用层相关的协议封装的真的是很好,以至于我看着看着发现看到什么代理啊路由啊TLS信息啊什么的,感觉这些实在是深入不下去了。如果以后真的要深入用到OkHttp且需要关系到这些的时候再来看好了。

Interceptor

中文解释是拦截器,它所实现的功能,就是对request和response拦截,进行相应的修改和处理之后得到我们更想要得到的结果。例如之前(二)里面看到的retryAndFollowUpInterceptor,就是一个重定向拦截器,当得到一个网络响应之后,通过判断responseCode来决定是否需要重定向到新的URL,自动发送新的HTTP请求,这样就免去了我们自己去进行这一部分的操作。另外,如果我们要增加缓存操作,也可以通过Interceptor来实现,如果缓存中存有对应请求的内容,就直接返回缓存中的response,反之在进行网络请求。我们也可以自定义Interceptor来实现自己想要的操作。不得不说这个设计真的很强。

流程图

首先先看一下上一篇提到过一个RealCall中用于网络请求的源码:

private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

这部分的chain.proceed()处理流程大致如下,箭头表示整个数据处理的走向:


在我看来有点像是一个栈,在处理request的时候interceptor依次入栈,然后出处理response的时候再依次出栈。接下来再看看一下里面是怎么处理的。


Interceptor接口

interceptor的作用,源码中是这么解释的(以下是Google翻译):观察,修改并潜在地缩短请求发出的请求并返回相应的响应。通常拦截器会在请求或响应中添加,删除或转换标头。

源码如下:

public interface Interceptor {
  
  //用于对request进行相应的处理,将处理后的request交给chain.proceed进行继续的网络请求
  //对chain.proceed返回的Response,进行相应的处理,将处理之后的Response返回
  Response intercept(Chain chain) throws IOException;

  //拦截链,一般Chain实例中会有一个list用于存储所有的interceptor,然后按照list的顺序访问每个interceptor
  interface Chain {
    //返回request
    Request request();
    
    //proceed会将request顺序地传递给每个拦截器进行相应的处理,
    //然后将response结果逆序的传递给每个拦截器进行相应的处理
    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

这里面Chain就是用于这种链式(我个人觉得像是栈式)结构的interceptor的实现。在OkHttp中,其实现类为RealInterceptorChain。

RealInterceptorChain

在RealInterceptorChain中,定义了一个list<Interceptor>用于存储所有的拦截器,然后在proceed方法中进行相应的调用。重点看一下proceed方法:

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

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
      Connection connection) throws IOException {

    if (index >= interceptors.size()) throw new AssertionError();
    //初始值为0,每次被调用会++
    calls++;

    // 用于保证httpStream会被使用,且拦截器必须保持host和port不变
    if (this.httpStream != null && !sameConnection(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // 保证每个interceptor只会调用一次proceed方法
    if (this.httpStream != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // 新实例化一个chain,并将index指向下一个interceptor
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpStream, connection, index + 1, request);
    //获取当前的interceptor
    Interceptor interceptor = interceptors.get(index);
    //调用interceptor的interceptor方法
    Response response = interceptor.intercept(next);

    // 保证下一个interceptor会调用且只调用一次proceed()方法
    if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // 保证response不为空
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }
    
    return response;
  }

整个代码也不多,主要是获取到List中当前位置的interceptor,然后新建一个RealInterceptorChain实例next,将index指向下一个interceptor,再调用当前Interceptor的intercept来获取到response,最后返回response。因为我们会向当前的interceptor传入新的next,所以在interecptor中会继续调用next.proceed()来将request传递给下一个interceptor,并通过next.proceed()返回下一个interceptor得到的response。通过这样来实现这种链式的操作。


拦截器大致的实现就讲完了,下面介绍一下之前提到过的一个Interceptor实现:retryAndFollowUpInterceptor

retryAndFollowUpInterceptor

这部分的源码呢,由于涉及到一些网络协议实现相关的东西,所以就大致讲一下一个实现流程吧,通过这个也能更好的理解一下interceptor的工作机制。最主要的方法自然是intercept,看一下源码:

  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    
    //streamAllocation主要是包含socket连接一个HTTP 请求/响应对
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()));

    int followUpCount = 0;
    Response priorResponse = null;
    //当没有抛出异常或者需要重定向的时候一直循环
    while (true) {
      //请求被取消
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      //判断是否需要释放streamAllocation,如果在获得响应的过程中出现了异常,需要释放streamAllocaion的资源
      boolean releaseConnection = true;
      try {
        //调用chain.proceed,进入后面的拦截器来对request进行拦截,并获得处理过后的response结果
        response = ((RealInterceptorChain) chain).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(), true, request)) throw e.getLastConnectException();
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        if (!recover(e, false, request)) throw e;
        releaseConnection = false;
        continue;
      } finally {
        // 判断是否需要释放资源
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      // 如果上一次的response不为空,将其添加到当前的response中(添加重定向之前的信息),或许是协议规定的?
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                .body(null)
                .build())
            .build();
      }
      
      //用于判断是否需要重定向,如果需要重定向,重新生成request
      Request followUp = followUpRequest(response);

      //不需要重定向,直接返回response
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }
      
      //需要重定向,先把当前的response.body中的流关闭,防止泄露
      closeQuietly(response.body());
      
      //重定向次数不能超过MAX_FOLLOW_UPS
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      //判断生成的请求是否可达
      if (followUp.body() instanceof UnrepeatableRequestBody) {
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }
      
      //如果重定向的url和host与之前的不相同,需要重新生成相应的streamAllocation
      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(
            client.connectionPool(), createAddress(followUp.url()));
      } else if (streamAllocation.stream() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      //保存当前的response作为重定向之前的response
      priorResponse = response;
    }
  }

这个的代码虽然不少,但是不难理解,主要就是获取到chain.proceed()返回的response,然后判断得到的responseCode来决定是否需要重定向,这部分主要是通过followUpRequest()方法来进行实现的,如果不需要重定向,则返回response,如果需要,则将followUpRequest()返回的resquest作为新的请求,重新传给chain.proceed(),通过这样来实现重定向的功能。

followUpRequest()方法是对responseCode进行判断,这里就不多讲了。

关于Interceptor的介绍大概就是这样了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值