OkHttp源码解析(二)——整体流程(下)

1.前言

这一篇将说一下请求体的写入以及相应的读取过程。

2.请求体写入过程

HttpEngine#readResponse

这个方法下有下面的一块代码片段。

else if (!callerWritesRequestBody) {
      networkResponse = new NetworkInterceptorChain(0, networkRequest,
          streamAllocation.connection()).proceed(networkRequest);
    } 

callerWritesRequestBody在初始化的时候为false,我们接着看NetworkInterceptorChain的proceed方法,会发现下面一段代码。


      if (permitsRequestBody(request) && request.body() != null) {

        Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());

        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

        request.body().writeTo(bufferedRequestBody);

        bufferedRequestBody.close();

      }
  • permitsRequestBody 判断是否允许body

  • 判断body是否为空

这里返回的requestBodyOut对象是对我们创建socket的时候的那个sike对象的包装。这里我偷个懒,具体的代码就不贴了。感兴趣的去http1xstream下追踪看看。这样,我们就把请求体也写入进去了。

3.读取响应的过程

HttpEngine#readNetworkResponse
在该方法中,有下面代码片段。

    Response networkResponse = httpStream.readResponseHeaders()
        .request(networkRequest)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    if (!forWebSocket) {
      networkResponse = networkResponse.newBuilder()
          .body(httpStream.openResponseBody(networkResponse))
          .build();
    }

我们重点关注httpStream.openResponseBody方法。

4.Http1xStream

Http1xStream#openResponseBody,该方法如下。

  @Override public ResponseBody openResponseBody(Response response) throws IOException {
    Source source = getTransferStream(response);
    return new RealResponseBody(response.headers(), Okio.buffer(source));
  }

在getTransferStream方法中,通过层层的调用,构造出一个Source对象,并将其和response.headers组装成真正的响应体。

5.Http2XStream

Http2xStream#openResponseBody

  @Override public ResponseBody openResponseBody(Response response) throws IOException {
    Source source = new StreamFinishingSource(stream.getSource());
    return new RealResponseBody(response.headers(), Okio.buffer(source));
  }

看到,关键点在于stream.getSource,而这个stream是什么呢?这是一个FramedStrem对象,并且是在writeRequestHeaders方法中初始化的。相应代码如下

stream = framedConnection.newStream(requestHeaders, permitsRequestBody, hasResponseBody);

而framedConnection是在HttpStrem#newStream方法中初始化并传入的

resultStream = new Http2xStream(this, resultConnection.framedConnection);

而resultConnection.framedConnection则是RealConnection的framedConnection了,这个对象是在什么时候初始化的呢?是在建立连接的时候,在establishProtocol方法中。

    if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) {
      socket.setSoTimeout(0); // Framed connection timeouts are set per-stream.

      FramedConnection framedConnection = new FramedConnection.Builder(true)
          .socket(socket, route.address().url().host(), source, sink)
          .protocol(protocol)
          .listener(this)
          .build();
      framedConnection.start();

      // Only assign the framed connection once the preface has been sent successfully.
      this.allocationLimit = framedConnection.maxConcurrentStreams();
      this.framedConnection = framedConnection;
    } else {
      this.allocationLimit = 1;
    }

注意,上面讲socket,source,sink都传了进去。
现实new了一个对象,病start,并且把这个对象传给RealConnection的framedConnection。因此,我们看下start方法干了什么,

 new Thread(readerRunnable).start();
readerRunnable = new Reader(variant.newReader(builder.source, client));

这里的Reader是一个Runnable是一个实现NamedRunnable的对象没我们需要看下他的execute方法。

    @Override protected void execute() {
      ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR;
      ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR;
      try {
        if (!client) {
          frameReader.readConnectionPreface();
        }
        while (frameReader.nextFrame(this)) {
        }
        connectionErrorCode = ErrorCode.NO_ERROR;
        streamErrorCode = ErrorCode.CANCEL;
      } catch (IOException e) {
        connectionErrorCode = ErrorCode.PROTOCOL_ERROR;
        streamErrorCode = ErrorCode.PROTOCOL_ERROR;
      } finally {
        try {
          close(connectionErrorCode, streamErrorCode);
        } catch (IOException ignored) {
        }
        Util.closeQuietly(frameReader);
      }
    }

可以在while里看到frameReader.nextFrame(this),而这个framewReader是Http2的Reader对象,我们看下他的方法。

variant = new Http2();
    Reader(BufferedSource source, int headerTableSize, boolean client) {
      this.source = source;
      this.client = client;
      this.continuation = new ContinuationSource(this.source);
      this.hpackReader = new Hpack.Reader(headerTableSize, continuation);
    }

而在在nextFrame方法里,我们就能看到读取的过程了。代码片段如下。

最后来张截图压压惊。
![这里写图片描述](https://img-blog.csdn.net/20160728000306967)
  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值