Android OkHttp源码解析入门教程(二)

本文深入探讨OkHttp的源码,解析其内部的拦截器和责任链模式。通过官方解释,了解到拦截器可用于监视、重写和重试网络请求。文章详细分析了OkHttp的系统内部拦截器,包括RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor和CallServerInterceptor的作用,揭示了OkHttp如何利用拦截器实现网络请求的处理和响应的转换。此外,还介绍了ConnectionPool的工作原理和连接复用的核心机制。
摘要由CSDN通过智能技术生成

概述

从OkHttp问世以来,度娘,google上关于OkHttp的讲解说明数不胜数,各种解读思想不尽相同,一千个读者就有一千个哈默雷特。本篇文章从源码出发向你介绍Okhttp的基本使用以及底层实现原理,让你从会写转向会用,学习Android顶尖源码的设计理念和开源扩展性,如果解读有误,还望提出探讨纠正。

文章链接:

Android OkHttp源码解析入门教程:同步和异步(一)

Android OkHttp源码解析入门教程:拦截器和责任链(二)

前言

上一篇文章我们主要讲解OkHttp的基本使用以及同步请求,异步请求的源码分析,相信大家也对其内部的基本流程以及作用有了大致的了解,还记得上一篇我们提到过getResponseWithInterceptorChain责任链,那么这篇文章就带你深入OkHttp内部5大拦截器(Interceptors)和责任链模式的源码分析,滴滴滴。

正文

首先,我们要清楚OkHttp拦截器(Interceptors)是干什么用的,来看看官网的解释

Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls
拦截器是一种强大的机制,可以做网络监视、重写和重试调用

这句话怎么理解呢?简单来说,就好比如你带着盘缠上京赶考,遇到了五批贼寇,但其奇怪的是他们的目标有的是为财,有的是为色,各不相同;例子中的你就相当于一个正在执行任务的网络请求,贼寇就是拦截器,它们的作用就是取走请求所携带的参数拿过去修改,判断,校验然后放行,其实际是实现了AOP(面向切面编程),关于AOP的了解,相信接触过Spring框架的是最熟悉不过的。

我们再来看官方给出的拦截器图
拦截器

拦截器有两种:APP层面的拦截器(Application Interception)、网络拦截器(Network Interception),而我们这篇文章注重讲解OkHttp core(系统内部五大拦截器)

那么OkHttp系统内部的拦截器有哪些呢?作用分别是干什么的?

紧接上篇文章,我们到getResponseWithInterceptorChain()源码中分析,这些拦截器是怎么发挥各自作用的?

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    // 责任链
    List<Interceptor> interceptors = new ArrayList<>();
    // 添加自定义拦截器(Application Interception)
    interceptors.addAll(client.interceptors()); 
    
    // 添加 负责处理错误,失败重试,重定向拦截器 RetryAndFollowUpInterceptor
    interceptors.add(retryAndFollowUpInterceptor);
  
    // 添加 负责补充用户创建请求中缺少一些必须的请求头以及压缩处理的 BridgeInterceptor
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    
    // 添加 负责进行缓存处理的 CacheInterceptor
    interceptors.add(new CacheInterceptor(client.internalCache()));
    
    // 添加 负责与服务器建立链接的 ConnectInterceptor
    interceptors.add(new ConnectInterceptor(client));
    
    if (!forWebSocket) {
     // 添加网络拦截器(Network Interception)
      interceptors.addAll(client.networkInterceptors()); 
    }
    //添加 负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor
    interceptors.add(new CallServerInterceptor(forWebSocket));
    
    // 将interceptors集合以及相应参数传到RealInterceptorChain构造方法中完成责任链的创建
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    // 调用责任链的执行
    return chain.proceed(originalRequest);
  }

从这里可以看出,getResponseWithInterceptorChain方法首先创建了一系列拦截器,并整合到一个Interceptor集合当中,同时每个拦截器各自负责不同的部分,处理不同的功能,然后把集合加入到RealInterceptorChain构造方法中完成拦截器链的创建,而责任链模式就是管理多个拦截器链。
关于责任链模式的理解,简单来说,其实日常的开发代码中随处可见

![](https://upload-images.jianshu.io/upload_images/7345598-6d623fc9d27ffbab.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ``` @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case 1: System.out.println("我是第一个拦截器: " + requestCode); break; case 2: System.out.println("我是第二个拦截器: " + requestCode); break; case 3: System.out.println("我是第三个拦截器: " + requestCode); break; default: break; } } } ``` 相信当你看到这段代码就明白责任链模式是干什么的吧,根据requestCode的状态处理不同的业务,不属于自己的任务传递给下一个,如果还不明白,你肯定是个假的程序员,当然这是个非常简化的责任链模式,很多定义都有错误,这里只是给大家做个简单介绍下责任链模式的流程是怎样的,要想深入并应用到实际开发中,还需要看相关文档,你懂我意思吧。
![](https://upload-images.jianshu.io/upload_images/7345598-667f554b7eaa040e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

我们直奔主题,上文也说到,getResponseWithInterceptorChain最终调用的RealInterceptorChain的proceed方法,我们接着从源码出发

public interface Interceptor {
  // 每个拦截器会根据Chain拦截器链触发对下一个拦截器的调用,直到最后一个拦截器不触发
  // 当拦截器链中所有的拦截器被依次执行完成后,就会将每次生成后的结果进行组装
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    // 返回请求
    Request request();
    
    // 处理请求
    Response proceed(Request request) throws IOException;
  }
}

public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final RealConnection connection;
  private final int index;
  private final Request request;
  private final Call call;
  private final EventListener eventListener;
  private final int connectTimeout;
  private final int readTimeout;
  private final int writeTimeout;
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
      EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
    this.call = call;
    this.eventListener = eventListener;
    this.connectTimeout = connectTimeout;
    this.readTimeout = readTimeout;
    this.writeTimeout = writeTimeout;
  }
   @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }
}

上述代码中可以看出Interceptor是个接口类,RealInterceptorChain实现了Interceptor.chain方法,在这里我们也看到了之前我们初始化时传进来的参数,可见我们实际调用的是RealInterceptorChain的proceed方法,接着往下看

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // 去掉一些逻辑判断处理,留下核心代码

    // Call the next interceptor in the chain.(调用链中的下一个拦截器)

    // 创建下一个拦截器链,index+1表示如果要继续访问拦截器链中的拦截器,只能从下一个拦截器访问,而不能从当前拦截器开始
    // 即根据index光标不断创建新的拦截器链,新的拦截器链会比之前少一个拦截器,这样就可以防止重复执行
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    // 获取当前拦截器
    Interceptor interceptor = interceptors.get(index);
    // 执行当前拦截器,并将下一个拦截器链传入
    Response response = interceptor.intercept(next);

    return response;
  }

看到这里你会觉得一头雾水,简单了解后,你的疑问可能如下
1.index+1的作用是干什么的?
2.当前拦截器是怎么执行的?
3.拦截器链是怎么依次调用执行的?
4.上面3点都理解,但每个拦截器返回的结果都不一样,它是怎么返回给我一个最终结果?

首先,我们要清楚责任链模式的流程,如同上文所说的,书生携带书籍,软银,家眷上京赶考,途遇强盗,第一批强盗抢走了书籍并且放行,第二批强盗抢走了软银放行,一直到最后一无所有才会停止。
书生就是最开始的RealInterceptorChain,强盗就是Interceptor,,index+1的作用就是创建一个新的拦截器链,简单来说,就是
书生(书籍,软银,家眷) →劫书籍强盗→书生(软银,家眷)→劫财强盗→书生(家眷)→…
即通过不断创建新的RealInterceptorChain链轮循执行interceptors中的拦截器形成一个责任链(抢劫链)模式直到全部拦截器链中的拦截器处理完成后返回最终结果

// 获取当前拦截器
Interceptor interceptor = interceptors.get(index);
index+1不断执行,list.get(0),list.get(1),… 这样就能取出interceptors中所有的拦截器,我们之前也说过Interceptor是一个接口,okhttp内部的拦截器都实现了这个接口处理各自的业务

如此,每当 Interceptor interceptor = interceptors.get(index)执行时,就可以根据interceptor.intercept(next)执行相应拦截器实现的intercept方法处理相应的业务,同时把创建好新的拦截器链传进来,这样就可以避免重复执行一个拦截器。

#RetryAndFollowUpInterceptor
负责处理错误,失败重试,重定向

/**
 * This interceptor recovers from failures and follows redirects as necessary. It may throw an
 * {@link IOException} if the call was canceled.
 */
public final class RetryAndFollowUpInterceptor implements Interceptor {
  /**
   * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,
   * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.
   */
  //最大失败重连次数:
  private static final int MAX_FOLLOW_UPS = 20;

  public RetryAndFollowUpInterceptor(OkHttpClient client, boolean forWebSocket) {
    this.client = client;
    this.forWebSocket = forWebSocket;
  }

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    // 建立执行Http请求所需要的对象,,通过责任链模式不断传递,直到在ConnectInterceptor中具体使用,
    // 主要用于 ①获取连接服务端的Connection ②连接用于服务端进行数据传输的输入输出流
    // 1.全局的连接池,2.连接线路Address,3.堆栈对象
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;  // 最终response
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        //  执行下一个拦截器,即BridgeInterceptor
       // 将初始化好的连接对象传递给下一个拦截器,通过proceed方法执行下一个拦截器链
      // 这里返回的response是下一个拦截器处理返回的response,通过priorResponse不断结合,最终成为返回给我们的结果
        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(), false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } cat
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值