您的位置:首页 > 理论基础 > 计算机网络

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

2016-07-18 14:53 295 查看
上一篇文章分析了
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: