本系列文章终于要到尾声了。上篇我们已经成功连接到服务了,那解析来应该做什么呢,相比聪明的你已经猜到了,那就是发送接收数据。
读写数据
- 第一步,写入请求头
- 第二步,写入请求头
- 第三步,读取响应头
- 第四步,读取响应体
前面我们已经说过,okhttp的流程是完全独立的,同样读写数据也是交给相关的类来处理,这个类就是HttpCodec(解码器)
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | @Override public Response intercept(Chain chain) throws IOException { HttpCodec httpCodec = ((RealInterceptorChain) chain).httpStream(); StreamAllocation streamAllocation = ((RealInterceptorChain) chain).streamAllocation(); Request request = chain.request(); // 第一步,写入请求头 long sentRequestMillis = System.currentTimeMillis(); httpCodec.writeRequestHeaders(request); Response.Builder responseBuilder = null; if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) { // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100 // Continue" response before transmitting the request body. If we don't get that, return what // we did get (such as a 4xx response) without ever transmitting the request body. if ("100-continue".equalsIgnoreCase(request.header("Expect"))) { httpCodec.flushRequest(); responseBuilder = httpCodec.readResponseHeaders(true); } //第二步,写入请求头 // Write the request body, unless an "Expect: 100-continue" expectation failed. if (responseBuilder == null) { Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength()); BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } } httpCodec.finishRequest(); // 第三步,读取响应头 if (responseBuilder == null) { responseBuilder = httpCodec.readResponseHeaders(false); } Response response = responseBuilder .request(request) .handshake(streamAllocation.connection().handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build(); // 第四步,读取响应体 int code = response.code(); if (forWebSocket && code == 101) { // Connection is upgrading, but we need to ensure interceptors see a non-null response body. response = response.newBuilder() .body(Util.EMPTY_RESPONSE) .build(); } else { response = response.newBuilder() .body(httpCodec.openResponseBody(response)) .build(); } if ("close".equalsIgnoreCase(response.request().header("Connection")) || "close".equalsIgnoreCase(response.header("Connection"))) { streamAllocation.noNewStreams(); } if ((code == 204 || code == 205) && response.body().contentLength() > 0) { throw new ProtocolException( "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength()); } return response; } |
写入请求头
每一次的网络请求必然携带有请求头,这是http协议里面的东西,建议大家对http相关知识要熟悉掌握,这对开发工作大有好处。
这里我们看上面第一步,调用到了,httpcodec的写入方法
1 | httpCodec.writeRequestHeaders(request); |
我们跟进去看看
1 2 3 4 5 6 7 | @Override public void writeRequestHeaders(Request request) throws IOException { // 创建一个请求行 比如GET /index.html HTTP/1.1。 String requestLine = RequestLine.get( request, streamAllocation.connection().route().proxy().type()); // 写入请求头 writeRequest(request.headers(), requestLine); } |
继续看writeRequest()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 此处有状态的判断 public void writeRequest(Headers headers, String requestLine) throws IOException { if (state != STATE_IDLE) throw new IllegalStateException("state: " + state); sink.writeUtf8(requestLine).writeUtf8("\r\n"); for (int i = 0, size = headers.size(); i < size; i++) { sink.writeUtf8(headers.name(i)) .writeUtf8(": ") .writeUtf8(headers.value(i)) .writeUtf8("\r\n"); } sink.writeUtf8("\r\n"); state = STATE_OPEN_REQUEST_BODY; } |
sink是Okio里面的内容,可以理解为outputstream,
- 首先写入请求行
- 循环写入请求头
完成后,会判断是否需要请求体,比如get就没有
写入请求体
1 2 3 4 5 6 7 | // 其他略 if (responseBuilder == null) { Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength()); BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut); request.body().writeTo(bufferedRequestBody); bufferedRequestBody.close(); } |
写入请求体比较简单,对于post的请求体本质上就是一个写入流,当然这里是Okio的东西,再次封装而已,通过连接成功后获取到的读写流,我们就可以把请求体写出。
写完后会有一个,
1 | finishRequest()// 时间上就是flush(),把流中的数据刷出去 |
读响应头
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | @Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException { if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) { throw new IllegalStateException("state: " + state); } try { // 首先解析相应行,协议,状态吗,响应头 StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict()); Response.Builder responseBuilder = new Response.Builder() .protocol(statusLine.protocol) .code(statusLine.code) .message(statusLine.message) .headers(readHeaders()); //响应头 if (expectContinue && statusLine.code == HTTP_CONTINUE) { return null; } state = STATE_OPEN_RESPONSE_BODY; return responseBuilder; } catch (EOFException e) { // Provide more context if the server ends the stream before sending a response. IOException exception = new IOException("unexpected end of stream on " + streamAllocation); exception.initCause(e); throw exception; } } |
解析相应头
1 2 3 4 5 6 7 8 9 10 | public Headers readHeaders() throws IOException { // 创建一个builder Headers.Builder headers = new Headers.Builder(); // parse the result headers until the first blank line // 一行行读出数据 for (String line; (line = source.readUtf8LineStrict()).length() != 0; ) { Internal.instance.addLenient(headers, line); } return headers.build(); } |
此处存放在一个list中
1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 key value key value ... Builder addLenient(String name, String value) { namesAndValues.add(name); namesAndValues.add(value.trim()); return this; } |
最后调用build(),转化成Headers。
读取响应体
解析完成头后会根据状态判断响应是否成功,
1 2 3 4 5 6 7 8 9 10 11 12 13 | int code = response.code(); // web socket if (forWebSocket && code == 101) { // Connection is upgrading, but we need to ensure interceptors see a non-null response body. response = response.newBuilder() .body(Util.EMPTY_RESPONSE) .build(); } else { // 响应成功 response = response.newBuilder() .body(httpCodec.openResponseBody(response)) .build(); } |
打开响应体
1 2 3 4 | @Override public ResponseBody openResponseBody(Response response) throws IOException { Source source = getTransferStream(response); return new RealResponseBody(response.headers(), Okio.buffer(source)); } |
注意:这里并没有解析,响应结果,而是关联起了一个Source,okio知识
至此整个请求过程完成。
然后往上一层拦截器返回结果,即回到上一个拦截器的intercept()方法,把响应结果交给上一个拦截器处理,比如缓存拦截器,会处理是不是要缓存下来等。
最后getResponseWithInterceptorChain()返回,我们再来看看这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor> interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); if (!forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket)); Interceptor.Chain chain = new RealInterceptorChain( interceptors, null, null, null, 0, originalRequest); return chain.proceed(originalRequest);//返回结果 } |
关于拦截器: 拦截器有点像上楼梯,一层层上去,让后在一层层倒回来,拿到结果给调用入口。
调用入口
我们再回过头来看看调用入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | @Override protected void execute() { boolean signalledCallback = false; try { // 通过一系列的拦截器最终难道结果 Response response = getResponseWithInterceptorChain(); // 中途取消, callback回调 if (retryAndFollowUpInterceptor.isCanceled()) { signalledCallback = true; responseCallback.onFailure(RealCall.this, new IOException("Canceled")); } else { // 否则调用成功 signalledCallback = true; responseCallback.onResponse(RealCall.this, response); } } catch (IOException e) { // 其他异常调用失败, if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { responseCallback.onFailure(RealCall.this, e); } } finally { // 最终一定会调用finished client.dispatcher().finished(this); } } } |
总结
通过本系列文章,你一定对Okhttp有了清晰的认识,至少大体流程会有一个清晰的脉络,至于有些细节,还需要聪明的你去,慢慢研读。本系列文章对细节并没有详细说明,我们学习的并不是具体的实现而是一个思路,真正的技术实现都是各个api的调用而已。Thanks!!!
系列文章