One Step By One Step 解析OkHttp3 - RealCall (二)
2016-07-18 14:53
295 查看
上一篇文章分析了
上面的代码讲解可能还未理解,下面祭出我的灵魂画作。
从
下面这个图的意思是一样的,看能不能更清楚点。
下面继续看
欢迎交流 QQ:2424334647
Dispatcher的作用,用于管理请求的状态,那么,真正负责发起请求,获取请求结果的,是谁呢?答案是真的请求,翻译成英文就是
RealCall(笑)。
Note: **Call** 和 **Request** 在本系列文章中,均被称为请求。 前者为OkHttp的请求(或者叫做命令), 后者为HTTP的请求的封装类。请读者自行区分。
概述
通常,我们需要发送一个请求,那么,我们会先使用创建一个Request,然后使用
okHttpClient.newCall(request),创建一个
Call,然后使用这个
Call,发送出(同步/异步)请求,没错,这个
Call,就是
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); }
上面的代码讲解可能还未理解,下面祭出我的灵魂画作。
从
getResponseWithInterceptorChain()进入到拦截器链中,然后请求被
ApplicationInterceptorChain乘载,传递到各个拦截器中,进入到所有的拦截器中后,调用
realCall.getResponse()获取到响应,然后再一层层往上返回。
下面这个图的意思是一样的,看能不能更清楚点。
下面继续看
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
相关文章推荐
- AFN 网络检测
- Ubuntu 16.04网络配置
- https
- 百度编辑器上传大容量视频报HTTP错误
- HTTP 头部详细解释
- c# 网络
- 【面试】HTTP post请求与get请求的区别
- Curl添加open ssl 实现https连接
- DWR框架简单实例 (http://my.oschina.net/u/1790925/blog/366346)
- https
- Nginx采用https加密访问后出现的问题
- uIP TCP/IP协议栈
- DWR入门教程(http://www.cnblogs.com/cyjch/archive/2012/02/16/2353758.html)
- Can you find it-上海网络赛
- 老男孩Linux高薪运维网络培训全程班3期 自我介绍和决心书
- 几种经典的网络服务器架构模型的分析与比较
- 理解HTTP/304响应
- 使用NSURLSession请求需要AD认证的HTTPS服务器
- Android网络编程概述
- HttpURLConnection处理post请求,返回字符串