安卓面试清单----OKHttp源码解析(三)


title: 安卓面试清单----OKHttp源码解析(三)
date: 2017-06-19 08:54:16
categories: 源码
tags: [Android,面试,OKHttp]
description: OKHttp源码从头解析,面试杀手锏


sendRequest ()

上篇文章我们讲了sendRequest ()方法,这节接着来看readResponse方法:

/**来自HttpEngine 类*/

 public void readResponse() throws IOException {
      if(this.userResponse == null) {
         if(this.networkRequest == null && this.cacheResponse == null) {
            throw new IllegalStateException("call sendRequest() first!");
         } else if(this.networkRequest != null) {
            Response networkResponse;
            if(this.forWebSocket) {
               this.httpStream.writeRequestHeaders(this.networkRequest);
               networkResponse = this.readNetworkResponse();
            } else if(!this.callerWritesRequestBody) {
             // 先执行拦截器,再写入request到HttpStream的Sinkbuffer中,最后发送buffer,并读取response
               networkResponse = (new HttpEngine.NetworkInterceptorChain(0, this.networkRequest)).proceed(this.networkRequest);
            } else {
               if(this.bufferedRequestBody != null && this.bufferedRequestBody.buffer().size() > 0L) {
               // 将request body的buffer发出去,这样requestBodyOut中就有了body
                  this.bufferedRequestBody.emit();
               }

               if(this.sentRequestMillis == -1L) {
                  if(OkHeaders.contentLength(this.networkRequest) == -1L && this.requestBodyOut instanceof RetryableSink) {
                     long responseCache = ((RetryableSink)this.requestBodyOut).contentLength();
                     this.networkRequest = this.networkRequest.newBuilder().header("Content-Length", Long.toString(responseCache)).build();
                  }

                  this.httpStream.writeRequestHeaders(this.networkRequest);
               }

               if(this.requestBodyOut != null) {
                  if(this.bufferedRequestBody != null) {
                     this.bufferedRequestBody.close();
                  } else {
                     this.requestBodyOut.close();
                  }

                  if(this.requestBodyOut instanceof RetryableSink) {
                // body 写入socket中   
                                this.httpStream.writeRequestBody((RetryableSink)this.requestBodyOut);
                  }
               }

               networkResponse = this.readNetworkResponse();
            }

            this.receiveHeaders(networkResponse.headers());
            if(this.cacheResponse != null) {
               if(validate(this.cacheResponse, networkResponse)) {
                  this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).headers(combine(this.cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();
                  networkResponse.body().close();
                  this.releaseStreamAllocation();
                  InternalCache responseCache1 = Internal.instance.internalCache(this.client);
                  responseCache1.trackConditionalCacheHit();
                  responseCache1.update(this.cacheResponse, stripBody(this.userResponse));
                  this.userResponse = this.unzip(this.userResponse);
                  return;
               }

               Util.closeQuietly(this.cacheResponse.body());
            }

            this.userResponse = networkResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();
            if(hasBody(this.userResponse)) {
               this.maybeCache();
               this.userResponse = this.unzip(this.cacheWritingResponse(this.storeRequest, this.userResponse));
            }

         }
      }
   }

定位到第8行:

this.httpStream.writeRequestHeaders(this.networkRequest);

writeRequestHeaders这个方法是HttpStream 接口的方法,由Http1xStream和Http2xStream重写。

如果采用http1.x协议,则执行Http1xStream里面的writeRequestHeaders方法,如果为http2.0,则执行Http2xStream的。由上文我们知道这取决于请求是http还是https。我们以1x为类:

/**来自 Http1xStream 类*/

public void writeRequestHeaders(Request request) throws IOException {
		this.httpEngine.writingRequestHeaders();
		String requestLine = RequestLine.get(request, this.httpEngine
				.getConnection().route().proxy().type());
		this.writeRequest(request.headers(), requestLine);
	}

首先得到请求的RequestLine(StatusLine),这个值由方法名、URL,Http协议拼接而成。
其次执行writeRequest方法:

/**来自 Http1xStream 类*/

public void writeRequest(Headers headers, String requestLine)
			throws IOException {
		if (this.state != 0) {
			throw new IllegalStateException("state: " + this.state);
		} else {
			this.sink.writeUtf8(requestLine).writeUtf8("\r\n");
			int i = 0;

			for (int size = headers.size(); i < size; ++i) {
				this.sink.writeUtf8(headers.name(i)).writeUtf8(": ")
						.writeUtf8(headers.value(i)).writeUtf8("\r\n");
			}

			this.sink.writeUtf8("\r\n");
			this.state = 1;
		}
	}

主要是将StatusLine和header信息写入sink,sink是什么呢,因为从上篇我们知道OKhttp底部是socket通信,所以sink就相当于我们在httpUrlConnection中使用的inputStream,它是socket的写入流,而source就是OutPutStream。

readNetworkResponse()

再看第9行:

/**来自 HttpEngine 类*/

private Response readNetworkResponse() throws IOException {
	 //对sink流执行flush操作
      this.httpStream.finishRequest();
      //等待服务器相应并读取服务器返回信息组装成我们需要的response
      Response networkResponse = this.httpStream.readResponseHeaders().request(this.networkRequest).handshake(this.streamAllocation.connection().handshake()).header(OkHeaders.SENT_MILLIS, Long.toString(this.sentRequestMillis)).header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())).build();
      if(!this.forWebSocket) {
         networkResponse = networkResponse.newBuilder().body(this.httpStream.openResponseBody(networkResponse)).build();
      }
      if("close".equalsIgnoreCase(networkResponse.request().header("Connection")) || "close".equalsIgnoreCase(networkResponse.header("Connection"))) {
         this.streamAllocation.noNewStreams();
      }

      return networkResponse;
   }

看下这个方法第7行是怎么组装的呢?

/**来自Http1xStream 类(下面是两个方法)*/

public Builder readResponseHeaders() throws IOException {
		return this.readResponse();
	}

	public Builder readResponse() throws IOException {
		if (this.state != 1 && this.state != 3) {
			throw new IllegalStateException("state: " + this.state);
		} else {
			try {
				StatusLine e;
				Builder exception1;
				 // 如果返回code不是100, 则直接将Response对象返回
        // 对于100,continue,server还会继续返回response string,我们需要在while循环中继续接收并解析
				do {
					e = StatusLine.parse(this.source.readUtf8LineStrict());
					 // 从输入流里读出答复并组装成答复消息,放入构造的Response的工厂类Build中
					exception1 = (new Builder()).protocol(e.protocol)
							.code(e.code).message(e.message)
							//答复头部的读取
							.headers(this.readHeaders());
				} while (e.code == 100);

				this.state = 4;
				return exception1;
			} catch (EOFException arg2) {
				IOException exception = new IOException(
						"unexpected end of stream on " + this.streamAllocation);
				exception.initCause(arg2);
				throw exception;
			}
		}
	}

上面主要是对答复头部的信息进行整理,而readNetworkResponse方法的第13行主要是对服务返回的body进行组装整理:

/**来自Http1xStream 类(下面是两个方法)*/

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

	private Source getTransferStream(Response response) throws IOException {
		if (!HttpEngine.hasBody(response)) {
			return this.newFixedLengthSource(0L);
		} else if ("chunked".equalsIgnoreCase(response
				.header("Transfer-Encoding"))) {
			return this.newChunkedSource(this.httpEngine);
		} else {
			long contentLength = OkHeaders.contentLength(response);
			return contentLength != -1L ? this
					.newFixedLengthSource(contentLength) : this
					.newUnknownLengthSource();
		}
	}
this.newChunkedSource(this.httpEngine);

这句代码最后执行到ChunkedSource (实现source接口)的 read方法:

	public long read(Buffer sink, long byteCount) throws IOException {
			if (byteCount < 0L) {
				throw new IllegalArgumentException("byteCount < 0: "
						+ byteCount);
			} else if (this.closed) {
				throw new IllegalStateException("closed");
			} else if (!this.hasMoreChunks) {
				return -1L;
			} else {
				if (this.bytesRemainingInChunk == 0L
						|| this.bytesRemainingInChunk == -1L) {
					this.readChunkSize();
					if (!this.hasMoreChunks) {
						return -1L;
					}
				}

				long read = Http1xStream.this.source.read(sink,
						Math.min(byteCount, this.bytesRemainingInChunk));
				if (read == -1L) {
					this.endOfInput(false);
					throw new ProtocolException("unexpected end of stream");
				} else {
					this.bytesRemainingInChunk -= read;
					return read;
				}
			}
		}

上面我们已经分析了Source是我们从服务读取的输入流,类似于OutPutStream,read方法则是从服务读取。

最终,回到readResponse方法的第9行,我们得到了完整的networkResponse。

我们再来看看validate(cacheResponse, networkResponse)方法是如何判断缓存是否可用的:

/**来自HttpEngine 类的ReadResponse方法*/

  private static boolean validate(Response cached, Response network) {
    //如果服务器返回304则缓存有效
      if(network.code() == 304) {
         return true;
      } else {
         Date lastModified = cached.headers().getDate("Last-Modified");
            //通过缓存和网络请求响应中的Last-Modified来计算是否是最新数据,如果是则缓存有效
         if(lastModified != null) {
            Date networkLastModified = network.headers().getDate("Last-Modified");
            if(networkLastModified != null && networkLastModified.getTime() < lastModified.getTime()) {
               return true;
            }
         }

         return false;
      }
   }

cache response存在的情况下,应该是缓存过期或者强制放弃缓存,在此情况下,缓存策略全部交给服务器判断,客户端只用发送条件get请求来验证cache的内容是否有变更即可,如果缓存是有效的,则返回304 Not Modifiled,且response中不会包含body,否则cache改变,回复200, OK。response中包含body。条件get请求有两种方式一种是Last-Modified-Date,一种是 ETag。这里采用了Last-Modified-Date,通过缓存和网络请求响应中的Last-Modified来计算是否是最新数据,如果是则缓存有效。

回到第一篇文章查找两个个参数 forWebSocket 和 callerWritesRequestBody,可以发现,这两个参数都为false,那么就是说
在readResponse方法中默认是不会执行第8、9行的,而是会去执行第11行,我们分析过发送请求时使用的拦截器模式,这里对答复的操作也用了同样的方式,不同于请求调用的是intercept,这里用的是proceed。所以我们有必要再分析以下这个拦截器,
重点是 proceed方法:

/**来自HttpEngine 的内部类 NetworkInterceptorChain 实现了Chain接口*/

  public Response proceed(Request request) throws IOException {
         ++this.calls;
         if(this.index > 0) {
            Interceptor response = (Interceptor)HttpEngine.this.client.networkInterceptors().get(this.index - 1);
            Address code = this.connection().route().address();
            if(!request.url().host().equals(code.url().host()) || request.url().port() != code.url().port()) {
               throw new IllegalStateException("network interceptor " + response + " must retain the same host and port");
            }

            if(this.calls > 1) {
               throw new IllegalStateException("network interceptor " + response + " must call proceed() exactly once");
            }
         }
         if(this.index < HttpEngine.this.client.networkInterceptors().size()) {
         //根据拦截器的数目取出拦截器并执行intercept里面用户自定义的处理方式,和我们之前分析过的一样
         //
            HttpEngine.NetworkInterceptorChain arg6 = HttpEngine.this.new NetworkInterceptorChain(this.index + 1, request);
            Interceptor arg9 = (Interceptor)HttpEngine.this.client.networkInterceptors().get(this.index);
            Response interceptedResponse = arg9.intercept(arg6);
            if(arg6.calls != 1) {
               throw new IllegalStateException("network interceptor " + arg9 + " must call proceed() exactly once");
            } else if(interceptedResponse == null) {
               throw new NullPointerException("network interceptor " + arg9 + " returned null");
            } else {
               return interceptedResponse;
            }
         } else {
             //写入请求头部
             HttpEngine.this.httpStream.writeRequestHeaders(request);
            HttpEngine.this.networkRequest = request;
			 //写入一些请求体
               Sink arg4 = HttpEngine.this.httpStream.createRequestBody(request, request.body().contentLength());
               BufferedSink arg7 = Okio.buffer(arg4);
               request.body().writeTo(arg7);
               arg7.close();
            }
			//将之前写入的数据flush给socket并读取服务器答复
            Response arg5 = HttpEngine.this.readNetworkResponse();
            int arg8 = arg5.code();
            if((arg8 == 204 || arg8 == 205) && arg5.body().contentLength() > 0L) {
               throw new ProtocolException("HTTP " + arg8 + " had non-zero Content-Length: " + arg5.body().contentLength());
            } else {
               return arg5;
            }
         }
      }

里面的writeRequestHeader方法和 readNetworkResponse 方法我们已经分析过了。再经过这么多7788的跳转、嵌套,终于拿到了我们需要的Response。最后回到我们第一篇文章最初的getResponse 方法,找到这两句:

			Response arg22 = this.engine.getResponse();
			Request arg23 = this.engine.followUpRequest();

第一句很明显是得到我们的response,直接返回userResponse。
第二句是对请求结果发生重定向时的处理,client发送一个request之后,server可能回复一个重定向的response,并在这个response中告知client需要重新访问的server的IP。此时,client需要重新向新的server发送request,并等待新server的回复。所以我们需要单独判断重定向response,并发送多次request。有了OKHttp,这一切你都不用管,它会自动帮你完成所有这一切。OKHttp中followUpRequest()方法就是完成这个功能的。

总结

OKHttp底层源码还是相当复杂的,毕竟它的功能如此之强大。OKHttp默认采用了Keep-Alive持久连接技术(并不代表一定长连接,取决于服务器),可支持gzip编码的response。在cache的处理上,如果cache可用,则直接使用cache,否则使用网络数据。OKHttp会做cache过期的判断和过期后的再验证。有了OKHttp,这一切你都不用管,它帮你cover掉了!

当需要做用户验证和重定向时,我们一般需要发送认证request,或向新server发送request,也就是要重新再生成新request并发送出去。有了OKHttp,这一切你都不用管,它又帮你cover掉了!

后期

1、研读几遍更新其中的错误和不足的点。
2、完善流程图,做到全面理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

q2nAmor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值