One Step By One Step 解析OkHttp3 - RealCall (二)

上一篇文章分析了Dispatcher的作用,用于管理请求的状态,那么,真正负责发起请求,获取请求结果的,是谁呢?答案是真的请求,翻译成英文就是RealCall(笑)。

Note: **Call** 和 **Request** 在本系列文章中,均被称为请求。
前者为OkHttp的请求(或者叫做命令),
后者为HTTP的请求的封装类。请读者自行区分。

概述


通常,我们需要发送一个请求,那么,我们会先使用创建一个 Request ,然后使用okHttpClient.newCall(request),创建一个Call,然后使用这个Call,发送出(同步/异步)请求,没错,这个Call,就是RealCall,不多说,下面开始看源码。

源码


先看RealCall的类结构

RealCall类结构

RealCall为接口Call的实现类。

/**
 * 一个Call封装一对Request和Response,能且仅能被执行一次。并且Call可以被取消。
 */
public interface Call {
  /** 返回初始化此Call的原始请求 */
  Request request();

  /**
   * 立即发出请求,一直阻塞到响应可以被处理,或者发生了错误。
   *
   * 可以调用Response#body方法获取到相应的body。为了连接服用,调用者需要调用ResponseBody#close()来关闭响应体。
   *
   * 注意:传输层成功(收到响应码,响应头,响应体),不代表应用层成功。依然可能有404,或者500这些坑爹的响应码。
   *
   * @throws IOException 如果一个请求因为被取消、连接问题、超时,那么抛出此异常。可能是服务器在发生错误之前接收到了请求,造成网络在交互过程中出错。
   * @throws IllegalStateException 当一个请求已经被执行,抛出此异常
   */
  Response execute() throws IOException;

  /**
   * 安排请求在未来的某一刻执行。
   *
   * OkHttpClient#dispatcher决定这个请求什么时候被执行:通常立即就被执行了,除非是目前有其它的请求被执行。
   * 根据上篇的分析,如果不能立即执行,会被移动到就绪队列中。
   *
   * 稍后,responseCallback会被调用,可能是一个正常的HTTP返回,或者是一个失败的异常。
   *
   * @throws IllegalStateException 当一个请求已经被执行,抛出此异常
   */
  void enqueue(Callback responseCallback);

  /** 取消请求,如果可能的话。如果请求已经有返回了,那么就不能被取消了。 */
  void cancel();

  /**
   * 返回true,如果 execute() 或者 enqueue(Callback) 被执行过了。执行请求两次会出错的,还是判断下好。
   */
  boolean isExecuted();

  /**
   * 返回true,如果这个请求被取消了。
   */
  boolean isCanceled();

  /**
    这是一个生成请求的工厂接口。
  */
  interface Factory {
    // 根据一个Http请求生成一个OKHttp请求。
    Call newCall(Request request);
  }
}
再来看RealCall中,这几个方法是如何实现的。

详细的分析过程,都写在了注释当中。

  /**
    构造器,原始的请求保存为成员变量
  */
  protected RealCall(OkHttpClient client, Request originalRequest) {
    this.client = client;
    this.originalRequest = originalRequest;
  }
/**
直接返回原始的请求
*/
@Override public Request request() {
  return originalRequest;
}
/**
同步执行请求,会造成线程阻塞

*/
@Override public Response execute() throws IOException {
    //如果已经被执行过了, 抛异常。
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      //设置该请求已经被执行。
      executed = true;
    }
    try {
      //这里调用的dispatcher的executed方法,将此请求加入到同步请求运行队列中
      client.dispatcher().executed(this);
      //下面的一行是此方法的核心,组成连接器链,将这个请求加入到拦截器链中
      //在所有的拦截器都拦截一遍后,发送http请求,获取response
      //下面会详细讲解这个方法
      Response result = getResponseWithInterceptorChain(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      //将运行中队列中的请求移除
      client.dispatcher().finished(this);
    }
  }
/**
进入到拦截器链中,并获取响应
*/
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
  // 新建一个ApplicationInterceptorChain实例,请求作为构造器参数传入
  Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
  // 调用此方法,进入拦截器链
  return chain.proceed(originalRequest);
}
ApplicationInterceptorChain
/**
ApplicationInterceptorChain 的构造器
index 为这一环节的索引
request 上一个环节拦截后的请求
*/
ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
  this.index = index;
  this.request = request;
  this.forWebSocket = forWebSocket;
}

/**
这个方法用于连接每个拦截器,并执行http请求
除了进入拦截器链的时候,由RealCall#getResponseWithInterceptorChain方法调用,
其它时刻都是在拦截器的Interceptor#intercept方法中,由用户主动调用此方法
因为需要实现拦截器的功能,必须要调用Interceptor#intercept,因此是用户参与了此拦截器链的建立
*/
@Override public Response proceed(Request request) throws IOException {
      // 如果此环节的索引没有超过拦截器的大小,则再建立一个ApplicationInterceptorChain对象,
      // 传递给Interceptor的intercept方法,实现递归调用
      if (index < client.interceptors().size()) {
        // 新建一个环节,并且将索引+1,这里的request可能不再是originalRequest了,因为在拦截器中可能被修改了
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        // 获取到对应的拦截器
        Interceptor interceptor = client.interceptors().get(index);
        // 执行拦截器的intercept方法,参数是上面新建的环节,这个方法里面会调用chain.proceed(),递归了。
        // 在最深的一层调用getResponse之后,响应会一层层的往外传
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }
        // 返回这一层拦截器处理用的响应
        return interceptedResponse;
      }

      // 递归到了最深的一层,拦截器都进入过了,发送httpRequest,获取到response.
      // 这个方法相当复杂, 因此放在最后讲解
      return getResponse(request, forWebSocket);
    }

上面的代码讲解可能还未理解,下面祭出我的灵魂画作。

ApplicationInterceptorChain

getResponseWithInterceptorChain()进入到拦截器链中,然后请求被ApplicationInterceptorChain乘载,传递到各个拦截器中,进入到所有的拦截器中后,调用realCall.getResponse()获取到响应,然后再一层层往上返回。

下面这个图的意思是一样的,看能不能更清楚点。
ApplicationInterceptorChain2

下面继续看RealCall的其它方法

/**
  调用的是下面的重载的方法
*/
@Override public void enqueue(Callback responseCallback) {
  enqueue(responseCallback, false);
}

/**
同样的,已经执行了,抛出异常
然后将此realCall包装为AsyncCall,调用Dispatcher#enqueue方法。
上一篇文章已经将结果Dispatcher了,这里就不再赘述。
*/
void enqueue(Callback responseCallback, boolean forWebSocket) {
    //如果已经被执行过了, 抛异常。
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    // 标识为已执行
    executed = true;
  }
  // AsyncCall是RealCall的内部类,相当于是对RealCall的一层封装,然后将其传入就绪或运行队列中,等待线程池执行请求
  // 下面会分析AsyncCall
  client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
AsyncCall

/**
  AsyncCall的代码较短,因此直接贴出整个类
  AsyncCall并不是Call接口的实现类,而是继承自NamedRunnable抽象类,而这个父类,只是一个带设置线程名称的Runnable而已。
  我们主要看execute()方法,这个方法是由Dispatcher中的线程池调用的。
*/
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private final boolean forWebSocket;

    private AsyncCall(Callback responseCallback, boolean forWebSocket) {
      super("OkHttp %s", originalRequest.url().toString());
      this.responseCallback = responseCallback;
      this.forWebSocket = forWebSocket;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    Object tag() {
      return originalRequest.tag();
    }

    void cancel() {
      RealCall.this.cancel();
    }

    RealCall get() {
      return RealCall.this;
    }

    /**
      过一遍拦截器链,并执行请求,然后调用回调函数。
    */
    @Override protected void execute() {
      // 保证onFailure最多只会被调用一次
      boolean signalledCallback = false;
      try {
        // 进入连接器链,并执行请求
        Response response = getResponseWithInterceptorChain(forWebSocket);
        // 如果请求被取消,调用onFailure
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          // 正常情况,调用onResponse
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        // 如果上面有调用过回调,就不调了,这里保证onFailure只会被调用一次
        if (signalledCallback) {
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        // 运行队列移除请求
        client.dispatcher().finished(this);
      }
    }
  }
  /**
   * 执行请求,并获取响应结果。分多钟情况:
      1. 请求被取消,抛出异常
      2. 需要重定向,如果没有超过最大次数,重现创建HttpEngine,再次发出请求;如果超过最大次数,抛出异常
      3. 连接出错,尝试恢复HttpEngine,再次发出请求;恢复失败,抛出异常
      4. 发送的请求有问题,直接抛出异常
   */
  Response getResponse(Request request, boolean forWebSocket) throws IOException {
    // 复制请求头,并设置适合的属性。如果长度不为-1,则设置Content-Length,否则使用chunked方式传输。
    // 如果对chunked不熟悉,请参考其他资料
    RequestBody body = request.body();
    if (body != null) {
      // 根据传进来的request创建新的Builder.
      Request.Builder requestBuilder = request.newBuilder();

      // 设置Content-Type
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      // 判断使用何种方式传输
      long contentLength = body.contentLength();
      // body长度不为-1,设置Content-Length
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        //  body 长度为 -1 ,使用chunked传输
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }

      //创建新请求
      request = requestBuilder.build();
    }

    // 创建一个新的引擎,每个引擎代表一次请求/响应对
    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = 0; //重试次数
    //死循环 出口:
    // 1. 请求被取消
    // 2. 请求有问题
    // 3. 捕获到异常,且尝试恢复失败
    // 4. 获取到响应,且无需重定向
    // 5. 重定向次数超过最大限制
    while (true) {
      // 被取消的情况
      if (canceled) {
        engine.releaseStreamAllocation();
        throw new IOException("Canceled");
      }

      boolean releaseConnection = true;
      try {
        // 发送请求
        engine.sendRequest();
        // 读取响应
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // 请求失败,请求本身有问题,或者是网络不通
        throw e.getCause();
      } catch (RouteException e) {
        // 连接到服务器的路由发生异常,请求还没被发送
        // 通过上一次连接异常恢复引擎
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);
        // 如果恢复成功,将当前的引擎设置为这个恢复好的引擎
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // 没法恢复,抛出异常
        throw e.getLastConnectException();
      } catch (IOException e) {
        // 与服务器交互失败,这时,请求可能已经被发送
        // 恢复引擎
        HttpEngine retryEngine = engine.recover(e, null);
        //如果恢复成功,将当前的引擎设置为这个恢复好的引擎
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // 没法恢复,抛出异常
        throw e;
      } finally {
        // 如果需要释放连接,则将连接释放
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }

      // 获取响应
      Response response = engine.getResponse();
      // 下一步的请求,如果存在,则需要重定向
      Request followUp = engine.followUpRequest();

      //如果不需要重定向
      if (followUp == null) {
        if (!forWebSocket) {
          // 释放连接
          engine.releaseStreamAllocation();
        }
        //返回响应
        return response;
      }

      // 如果需要重定向,关闭当前引擎
      StreamAllocation streamAllocation = engine.close();

      // 如果超过最大数,释放连接,并抛出异常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        // 释放连接
        streamAllocation.release();
        // 抛出异常
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      // 如果重定向的地址和当前的地址一样,则不需要释放连接
      if (!engine.sameConnection(followUp.url())) {
        streamAllocation.release();
        streamAllocation = null;
      }

      // 使用重定向后的请求,重新实例一个引擎,开始下一次循环
      request = followUp;
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }
  }

总结


RealCall的作用一句话概括——发送请求。其中细节有拦截器的建立过程,异步回调的调用,HttpEngine的使用过程。
HttpEngine的代码没有往里Step in,本系列以后的文章会有分析。

欢迎交流 QQ:2424334647

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值