Okhttp-interceptor源码分析,快上车!

本文详细解析了 OkHttp 中拦截器的工作原理及其调用顺序,深入探讨了责任链模式在 OkHttp 拦截器中的应用,并通过实例代码展示了拦截器的具体实现。

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

	前言:被一位阿里面试官问到过这个问题,“让我谈一下okhhtp拦截器的调用顺序及影响。”
	我脑子里只有责任链的设计模式,但是几种拦截器的顺序我确实没记忆过,有点尴尬。
	所以呢,就来稍微研究一下,顺便记忆一下,正所谓“面试造航母,工作拧螺丝。”
复制代码

1)基本用法

此次我看的源码是v3.9.0, 首先我们来看一下官网上基本用法(get请求为例),如下

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
      .url(url)
      .build();
  Response response = client.newCall(request).execute();
  String jsonStr=response.body().string()

复制代码

可以看到主要分四步:1.构建OkHttpClient对象;2.构建Request对象;3.调用client.newCall(request)得到Call对象;4.Call对象执行同步方法execute(),或者异步方法enqueue(Callback responseCallback),得到响应的内容.

2)请求过程源码分析

2.1 OkhttpClient构建

 public OkHttpClient() {
    this(new Builder());
  }

  OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

...剔除部分源码

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;
....
  }


 public static final class Builder {
    Dispatcher dispatcher;
    @Nullable Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable Cache cache;
    @Nullable InternalCache internalCache;
    SocketFactory socketFactory;
    @Nullable SSLSocketFactory sslSocketFactory;
    @Nullable CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;

    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      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;
    }
 Builder(OkHttpClient okHttpClient) {
      this.dispatcher = okHttpClient.dispatcher;
      this.proxy = okHttpClient.proxy;
      this.protocols = okHttpClient.protocols;
      this.connectionSpecs = okHttpClient.connectionSpecs;
      this.interceptors.addAll(okHttpClient.interceptors);
      this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
      this.eventListenerFactory = okHttpClient.eventListenerFactory;
      this.proxySelector = okHttpClient.proxySelector;
      this.cookieJar = okHttpClient.cookieJar;
      this.internalCache = okHttpClient.internalCache;
      this.cache = okHttpClient.cache;
      this.socketFactory = okHttpClient.socketFactory;
      this.sslSocketFactory = okHttpClient.sslSocketFactory;
      this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
      this.hostnameVerifier = okHttpClient.hostnameVerifier;
      this.certificatePinner = okHttpClient.certificatePinner;
      this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
      this.authenticator = okHttpClient.authenticator;
      this.connectionPool = okHttpClient.connectionPool;
      this.dns = okHttpClient.dns;
      this.followSslRedirects = okHttpClient.followSslRedirects;
      this.followRedirects = okHttpClient.followRedirects;
      this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
      this.connectTimeout = okHttpClient.connectTimeout;
      this.readTimeout = okHttpClient.readTimeout;
      this.writeTimeout = okHttpClient.writeTimeout;
      this.pingInterval = okHttpClient.pingInterval;
    }

 public OkHttpClient build() {
      return new OkHttpClient(this);
    }
}
复制代码

可以看到Client对象是通过建造者模式设计的,直接new OkhttpClient()方式,传入默认的builder,或者通过配置builder,调用build()构建;

2.2 client.newCall(Request request

Request对象也是通过建造者模式构建的,根据不同的请求构建不同的Request对象;

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
/**
我们可以看到,client调用.newCall(request),实际调用的是RealCall.newRealCall(this, request, false),返回Call对象,说明Realcall应该是实现了Call接口;
在去看一下RealCall.newCall都干了些什么;
*/

final class RealCall implements Call {
...

 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
   
//可以看到RealCall实现了Call接口,newRealCall 内部实际是将入参传入构造,构建了RealCall对象,创建一个call的事件监听;
	在看看RealCall构造干了什么;
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

 private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
	//内部将入参client,原始的请求赋值,同时构建了RetryAndFollowUpInterceptor()拦截器;
	//该拦截器的作用就是发生错误时,进行重定向;

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }


}

 
复制代码
2.3 call.excute() call.enqueue(callback)

我们继续跟进拿到call对象后,真正发起网络请求的操作 继续看call的实现类RealCall

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();

	//开始监听call请求

    eventListener.callStart(this);
    try {

	//通过client的dispatcher,分发请求;Dispather中构建了一个线程池,所有的请求会加入到线程池中执行;
//这里

      client.dispatcher().executed(this);

	//这个方法很关键,也是Okhttp拦截器的责任链调用的开始,是这个库的精华所在,可以看到,在这里返回了服务器响应的内容
	//我已经迫不及待一探究竟了,哈哈哈哈哈哈= =
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }


  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
//开始监听
    eventListener.callStart(this);
//异步调用


    client.dispatcher().enqueue(new AsyncCall(responseCallback));

/**
*dispatcher对象的enqueue方法,可以看到,如果保存异步Call的集合小于最大请求数,并且未达到同一个host最大的连接数
*那么直接加入待执行队列,调用execute,执行;否则,先加入预执行队列
*/
synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  }

复制代码
2.4 拦截器责任链的调用 getResponseWithInterceptorChain()

为了方便大家理解,请允许我先上个图:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
//1.构建一个空集合interceptors
    List<Interceptor> interceptors = new ArrayList<>();

//2.可以看到首先会将client的interceptors全部加入这个集合头部,这个client的interceptors就是我们在构建Client Builder时,调用
// addInterceptor(Interceptor interceptor)加入的自定义拦截器,也就是所谓的应用拦截器;
    interceptors.addAll(client.interceptors());
//3.尾部追加重定向拦截器 retryAndFollowUpInterceptor,还记得这个拦截器是什么时候创建的吗?
//没错,就是在new RealCall的时候构建的.
    interceptors.add(retryAndFollowUpInterceptor);
//4.尾部添加BridgeInterceptor,设置默认的cookjar,这是一个请求和响应的连接器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
//5.添加缓存拦截器,处理一些网络缓存逻辑,控制走网咯还是走缓存等
    interceptors.add(new CacheInterceptor(client.internalCache()));
//6.添加网络连接拦截器
    interceptors.add(new ConnectInterceptor(client));
//7.如果不是websocket,还添加了网络拦截器,添加自定义连接拦截器(比如我们常用的facebook调试框架的开源框架stecho)
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
//8.添加了请求服务拦截器
    interceptors.add(new CallServerInterceptor(forWebSocket));
// 以上是存放在arryList中intercptor的顺序,
//9.可以看到接下来就是构建责任链Chain, 其实现类是RealInterceptorChain,
// RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
//      HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
//     EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) 
//10.其中,我们最主要来关注一下 index这个入参
//可以看到,链的开始index入参为0,我们继续跟进一下chain.proceed(originalRequest)干了什么;

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }
/*==========================*/
  public Builder addInterceptor(Interceptor interceptor) {
      if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
      interceptors.add(interceptor);
      return this;
    }
  public List<Interceptor> interceptors() {
    return interceptors;
  }

//进入Chain的实现类RealInterceptorChain,看一下proceed做了哪些事情
//我们看一些关键的地方.

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
//11.首先判断index是否超出拦截器索引,超出就跑异常,终止责任链
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpCodec != null && !this.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.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // 12.构建下一个chain对象,可以看到此处index+1,
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);

//13.从集合中取出下一个拦截器,调用 interceptor.intercept(chain)返回Response;我们可以随便看一个interceptor的实现类,
//研究一下interceptor.intercept(chain) 是如何返回Response的;

    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpCodec != 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;
  }

复制代码
2.5 拦截器责任链的调用 interceptor.intercept(chain)

我们就以CacheInterceptor为例,看看它的intercept() fun都做了哪些事情.

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

...省略部分源码,我们只看关键代码

//1.可以看到这里判断了网络请求是否存在,是否有响应缓存,两则都null,时,构建一个504错误码的Resonse并返回
// 该方法return
    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();
    }

    // 2.判断无请求网络,返回缓存内容,方法return
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
	

//3.上述两种情况都不满足,走到这里,调用chain.proceed(request)
//看到这个Api是否似曾相识呢~~哈哈哈哈哈,回到getResponseWithInterceptorChain()方法,其就是调用了
//chain.proceed(originalRequest)得到Response返回;
//而,chain.proceed 中如上分析,会把index++,构建下一chain,至此,责任链就完整形成了.
//前一个chain.proceed 返回值依赖后一个chain.proceed返回,由此递归,直到index 超出抛出超异常.

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

  ....省略代码

    return response;
  }
复制代码

3)总结

我们可以看到Okhttp在构建Client和Request对象时用到了Builder模式,这才我们开发中也使用场景也很多,尤其是参数很多的情况下,通过构造或者set方法初始化就不是特别友好;Interceptor的责任链模式设计模式也很巧妙,我们可以通过很轻松的添加或删除拦截器实现许多业务场景,但是细想一下也不难发现,责任链模式如果链太长的情况下会发生什么?方法不断压栈,只有链调用结束或者异常时,才会出栈,释放栈空间. 有哪里写错或理解错误的欢迎讨论指出~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值