理解 OkHttp 的拦截器

375 阅读3分钟

OkHttp 最精髓的地方就是它的拦截器,把网络请求的操作解耦,每个拦截器只负责自己的工作,并且可以根据自己的需求自定义添加拦截器。第一次看到拦截器那段代码我是懵逼的。

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

乍一看,就是把一堆拦截器添加到了列表中,然后 new 了一个 Chain 对象启动了一下,这就构成了可以拦截请求拦截响应的拦截器链吗?翻看源码发现主要是通过递归调用函数栈来实现这种往下拦截请求,往上拦截响应的。

我模仿源码拦截器写了一段伪代码方便理解拦截器的工作流程。

首先定义拦截器接口和连接拦截器的锁链接口。

public interface MyInterceptor {
  /**
   * 拦截
   *
   * @param chain 下一个锁链
   */
  String intercept(MyChain chain);

  interface MyChain {
    /**
     * 获取请求
     */
    String request();

    /**
     * 处理请求返回一个结果
     */
    String process(String request);
  }
}

A、B、C 三个拦截器的实现,各自拦截请求和响应

public class InterceptorA implements MyInterceptor {
  @Override public String intercept(MyChain chain) {
    // 修改请求
    String request = chain.request();
    String newRequest = request + "/a";
    Log.d("InterceptorA", "newRequest = " + newRequest);
    // 调用第2个链子处理新的请求,返回处理后的结果
    String response = chain.process(newRequest) + "/responseA";
    Log.d("InterceptorA", "response = " + response);

    return response;
  }
}

public class InterceptorB implements MyInterceptor {
  @Override public String intercept(MyChain chain) {
    // 修改请求
    String request = chain.request();
    String newRequest = request + "/b";
    Log.d("InterceptorB", "newRequest = " + newRequest);
    // 处理请求
    String response = chain.process(newRequest) + "/responseB";
    Log.d("InterceptorB", "response = " + response);

    return response;
  }
}

public class InterceptorC implements MyInterceptor {
  @Override public String intercept(MyChain chain) {
    // 修改请求
    String request = chain.request();
    String newRequest = request + "/c";
    Log.d("InterceptorC", "newRequest = " + newRequest);
    // 处理请求
    String response = chain.process(newRequest) + "/responseC";
    Log.d("InterceptorC", "response = " + response);

    return response;
  }
}

锁链的实现:

public class RealChain implements MyInterceptor.MyChain {
  private final String request;
  private final int index;
  private final List<MyInterceptor> interceptors;

  public RealChain(String request, int index, List<MyInterceptor> interceptors) {
    this.request = request;
    this.index = index;
    this.interceptors = interceptors;
  }

  @Override public String request() {
    return request;
  }

  @Override public String process(String request) {
    if (index == interceptors.size()) {
      return "请求结束,得到结果:";
    }
    // 构建下一个锁链实例,处理下一个拦截器
    RealChain next = new RealChain(request, index + 1, interceptors);
    // 得到当前的拦截器
    MyInterceptor interceptor = interceptors.get(index);
    // 当前拦截器进行拦截得到处理结果,将结果返回
    return interceptor.intercept(next);
  }
}

关键是调用的时候,从第一个锁链开始往下递归依次调用拦截器的拦截方,按 A ->B->C 的顺序拦截请求。调用到最后一个拦截器时开始往回按 C->B->A 返回结果,这样就构成了官方拦截器图的效果。

  String getMyInterceptorChain() {
    List<MyInterceptor> interceptors = new ArrayList<>();
    interceptors.add(new InterceptorA());
    interceptors.add(new InterceptorB());
    interceptors.add(new InterceptorC());

    String originRequest = "www.baidu.com";
    // 构建出锁链
    RealChain chain = new RealChain(originRequest, 0, interceptors);
    return chain.process(originRequest);
  }

运行后打印日志,请求是按ABC的顺序拦截添加的,返回结果则是CBA的顺序拦截添加的。

newRequest = www.baidu.com/a
newRequest = www.baidu.com/a/b
newRequest = www.baidu.com/a/b/c
response = 请求结束,得到结果:/responseC
response = 请求结束,得到结果:/responseC/responseB
response = 请求结束,得到结果:/responseC/responseB/responseA

函数调用顺序:

链子A处理 -> 拦截器A拦截 -> 链子B处理 -> 拦截器B拦截 -> 链子C处理 -> 拦截器C拦截 -> 链子C处理 -> end