彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法
彻底掌握网络通信(十一)HttpURLConnection进行网络请求的知识准备
彻底掌握网络通信(十二)HttpURLConnection进行网络请求概览
彻底掌握网络通信(十三)HttpURLConnection进行网络请求深度分析
彻底掌握网络通信(十四)HttpURLConnection进行网络请求深度分析二:缓存
彻底掌握网络通信(十五)HttpURLConnection进行网络请求深度分析三:发送与接收详解
彻底掌握网络通信(十六)走进OkHttp3的世界(一)引言
彻底掌握网络通信(十七)走进OkHttp3的世界(二)请求/响应流程分析
我们知道当我们实例化一个call之后(具体实现为RealCall),其会调用RealCall的enqueue方法,
//RealCall.java
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
在这个方法中,我们会实例化AsyncCall,这个AsyncCall有几个特点:
- AsyncCall为RealCall的内部类,顾其能拿到外部类的重要属性,如可以拿到我们发起的请求Request
- AsyncCall实现了Runnable接口,每一个http请求的执行其实就是AsyncCall的execute方法被执行
实例化AsyncCall之后,会获得请求分发者Dispatcher对象,并调用Dispatcher的enqueue方法
//Dispatcher.java
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
第3行~第5行:如果runningAsyncCalls队列大小小于64并且如果同一个host的请求数小于5的情况下,会将当前的call添加到runningAsyncCalls队列中,并在线程池中来执行这个call
第6行~第7行:相反的则将当前call添加到readyAsyncCalls中
在 **Dispatcher**中,有几个重要属性我们需要重点看下
- readyAsyncCalls
- runningAsyncCalls
- runningSyncCalls
- cancelAll()方法
- promoteCalls()方法
- finished()方法
- queuedCalls()方法
- runningCalls()方法
在enqueue方法中我们知道
- runningAsyncCalls添加的是正在执行的call
- readyAsyncCalls是当队列内容过多,正在排队的call
疑问1:runningSyncCalls是什么?
我们先看下如下代码
OkHttpClient mOkHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin")
.addHeader("Accept", "application/json; q=0.5")
.build();
Call call = mOkHttpClient.newCall(request);
call.execute();
这段代码中,我们直接调用了call的execute方法
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
第9行:调用Dispatcher的executed方法,executed方法作用就是将当前call添加到runningSyncCalls列表中;
第10行:通过getResponseWithInterceptorChain方法获取响应;这里也是很关键的方法,会在后面详述
顾 runningSyncCalls 代表的是一个同步执行call的列表,每一个同步执行的call都会被添加到这个列表中
1: Dispatcher.java cancelAll() 详解
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.get().cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.get().cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
该方法会调用call的cancel方法
@Override public void cancel() {
retryAndFollowUpInterceptor.cancel();
}
我们知道call的实现者为RealCall,在实例化RealCall的时候,我们便会创建 RetryAndFollowUpInterceptor 这样一个实例,这个类代表一个请求的重试和重定向能力,如果调用retryAndFollowUpInterceptor的cancel方法则表示取消当前请求,并关闭当前socket和释放资源
顾 cancelAll方法作用就是取消全部的正在运行的,等待运行的请求
2: Dispatcher.java promoteCalls() 详解
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
关键代码就在第5行~第12行
第5行: 遍历等待运行的call
第8行~第11行:如果一个host上同时请求的call小于5个,则将此call从等待执行的队列中移除,并添加到正在运行的请求队列中,同时运行该call
顾 promoteCalls方法作用就是将等待执行的call从等待队列中移除,并将该call添加到正在执行的队列当中,并开始执行这个call
3: Dispatcher.java finished() 详解
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
第14行,如果将call从runningAsyncCalls移除失败,抛出一个error
第15行,参考上面的分析
第16行,获得同步或者异步请求的总数量
第17行,对一个runable进行赋值
第21行,当所有请求都执行完成之后,这个idleCallback的run方法将被执行
疑问2:idleCallback怎么使用尼?
这个idleCallback是一个Runnable类型,他使用的场景如下
当客户端发起N多个网络请求,当这些请求全部被执行完之后,这个idleCallback的run方法将会被回调
OkHttpClient mOkHttpClient = new OkHttpClient();
mOkHttpClient.dispatcher().setIdleCallback(new Runnable() {
@Override
public void run()
//当run方法被执行的时候,则说明所有的请求都执行完毕了
}
});
4: Dispatcher.java queuedCalls() 详解
public synchronized List<Call> queuedCalls() {
List<Call> result = new ArrayList<>();
for (AsyncCall asyncCall : readyAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
这段代码比较简单,就是返回全部的异步请求列表
5: Dispatcher.java runningCalls() 详解
public synchronized List<Call> runningCalls() {
List<Call> result = new ArrayList<>();
result.addAll(runningSyncCalls);
for (AsyncCall asyncCall : runningAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
返回全部的同步请求列表
疑问:怎么取消一个正在执行的请求?
我们知道我们可以通过queuedCalls或者runningCalls方法获得全部的异步或者同步请求列表,而call提供了cancel的方法来取消一个请求
那么具体的代码就是这样的
OkHttpClient mOkHttpClient = new OkHttpClient();
mOkHttpClient.dispatcher().queuedCalls().get(0).cancel();
当然了这个是比较粗糙的方法,精细点的你可以通过如下代码来取消操作
OkHttpClient mOkHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.tag("request1")
.url("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin")
.addHeader("Accept", "application/json; q=0.5")
.build();
for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
if ("request1".equals(call.request().tag())) {
call.cancel();
}
}
备注:这里的取消请求,并非是把发出去的请求拉回来,而是让这个请求回调onFail方法
简单总结下:
Dispatcher类作为一个分发者,
- 在其内部保持三个重要的队列,分别用来存储异步请求,同步请求和超出异步队列大小的待执行队列
- 当实例化call之后,便会添加到对应队列中
- Dispatcher里面维护的线程池可以使异步call执行起来
- 同时Dispatcher提供了查询全部请求的能力,和取消全部请求的能力等等
- 最后一个比较偏门的地方就是,他提供了一个Runable类型的idleCallback,如果所有的请求均完成,该runable的run方法即可被执行
当创建 RealCall 之后,我们就可以将这个请求发送出去了,这里以一个异步的call发送过程为例子进行分析
RealCall内部有几个重要属性
- OkHttpClient client
- RetryAndFollowUpInterceptor retryAndFollowUpInterceptor //重试,重定向的拦截器
- Request originalRequest //发送的请求
在RealCall构造函数中,会完成上述属性的赋值,其中forWebSocket为false
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
一个异步请求其本质上是RealCall的内部类AsyncCall的执行,当一个异步请求被Dispatcher中的线程池执行的时候,AsyncCall类的execute方法将会被执行
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
第4行: 获得服务端的响应
第5行:当客户端调用call的cancel方法之后,此处即为true,回调onFailure方法
第10行:请求完成之后,回调onResponse方法
第21行:当请求完成之后,调用Dispatcher的finish方法,将此请求从runningAsyncCalls队列中移除,并从readyAsyncCalls队列中获取一个等待请求,并发起该请求
我们重点看下第4行的方法getResponseWithInterceptorChain()
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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
在这段代码中,集合interceptors添加了很多了内容,他们有
- client.interceptors()
- retryAndFollowUpInterceptor
- BridgeInterceptor
- CacheInterceptor
- ConnectInterceptor
- client.networkInterceptors()
- CallServerInterceptor
统统7个内容,而这几个内容也是贯穿OkHttp的整个环节中重要的类;我们应该详细了解一下
在 RealCall的构造函数中,已经创建RetryAndFollowUpInterceptor类的实例,顾我们先看下这几个拦截器的继承关系
可见这些拦截器均实现了 Interceptor 接口
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
所有的拦截器都实现了Interceptor 接口,也就是说每一个拦截器都会触发intercept方法;
第2行:定义了intercept方法,传入Chain作为参数
第4行,定义了Chain接口,这个接口有几个关键方法
- Response proceed()
- Request request()
- Connection connection()
这样的定义的好处是什么?我们返回到getResponseWithInterceptorChain方法的第14行,他创建了一个RealInterceptorChain的实例,很明显这个类实现了Interceptor.Chain接口;
第15行,调用Interceptor.Chain接口的proceed方法返回请求响应
这里不经就产生了一个疑问,这个 RealInterceptorChain 是做什么用的,他和拦截器有什么关系
为了了解这个问题的答案,我们看下RealInterceptorChain类持有的几个重要成员
- private final List interceptors;
- private final StreamAllocation streamAllocation;
- private final HttpCodec httpCodec;
- private final RealConnection connection;
- private final int index;
- private final Request request;
- private final Call call;
第一个成员就是他持有全部的拦截器,通过这个类可以获得全部的拦截器
第二个成员他是打通客户端到服务端socket连接并保持流的关键类
第四个成员这个就跟我们之前的HttpUrlConncetion里面一样,他是一个客户端到服务端的连接
第五个成员便是用来从interceptors列表中获得拦截器的索引
接着分析下这个类的proceed方法
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
第7行,如果index超过interceptors集合大小,显然或得不到拦截器,顾抛出异常
第24行,创建RealInterceptorChain实例,此时传入的第五个参数值为1,
第27行,通过index获得第一个拦截器
第28行,传入刚刚创建的RealInterceptorChain实例,调用拦截器的intercept方法
我们以RetryAndFollowUpInterceptor的intercept方法为例,看下这个RealInterceptorChain的工作流程是什么
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Call call = realChain.call();
EventListener eventListener = realChain.eventListener();
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
/* 省略部分代码*/
Response response;
try {
response = realChain.proceed(request, streamAllocation, null, null);
} catch (RouteException e) {
}
}
大家注意第14行,纵观所有的拦截器,在拦截器的intercept方法中,均会调用chain的proceed方法;
这里对RealCall和RealInterceptorChain做一个总结:
- 当call被执行的时候,getResponseWithInterceptorChain方法将会被执行
- getResponseWithInterceptorChain会添加7中类型的拦截器,每个拦截器实现了Interceptor接口
- getResponseWithInterceptorChain第一次创建实现了Chain接口的类RealInterceptorChain,RealInterceptorChain通过索引可以获得全部的拦截器
- 通过调用RealInterceptorChain类的proceed方法将所有的拦截器功能运行起来
4.1 再次构造一个RealInterceptorChain实例,其中获得拦截器的索引+1,这样既可获得下一个拦截器
4.2 调用拦截器的intercept方法
4.3 拦截器的intercept方法又会回调RealInterceptorChain的proceed方法,通过这样的循环,所有的拦截器功能就执行起来了
那我们不经又问了这些拦截器都会被执行吗?一个响应必须是让全部的拦截器都执行完毕吗?
不是的,
我们知道RealInterceptorChain维护了7种拦截器,
如果一个请求在 没有缓存的 情况下,一个call被执行后,其流程是
- 创建RealInterceptorChain实例,并调用其proceed方法
- 在proceed方法里面,再次创建一个RealInterceptorChain,第一步和第二步的实例区别在于第五个参数的值不同,第二个实例中第五个参数值+1了,这样就能获得下一个拦截器
- 获得第一个拦截器,(我们这里以RetryAndFollowUpInterceptor为第一个拦截器为例子),将第二个RealInterceptorChain作为参数传入拦截器的intercept方法中,并执行该方法
- 第一个拦截器在执行自己的intercept方法之后,会使用传入的RealInterceptorChain参数,调用其proceed方法
这样重复第2步,这样一个链式结构的请求就结束了
那一个请求的response是如何返回的 经过上面的分析,每一个拦截器的intercept都会返回一个response,同时拦截器是自上而下执行的,顾这个response便会采用冒泡的方式逐步返回RealCall;顾我们倒着看拦截器的执行就能知道response是如何返回的了
- CallServerInterceptor : 负责和服务端进行交互,拿到请求的响应
- ConnectInterceptor: 负责和服务端进行连接,创建socket连接
- CacheInterceptor: 对上面的拿到的response进行缓存处理
- BridgeInterceptor: 对上层发起的请求做一次补充处理,补充缺少的头信息,然后传给下一个拦截器进行请求;同时当response返回的时候,对response做一次处理,然后返回给retryAndFollowUpInterceptor
- retryAndFollowUpInterceptor:开启一个while循环,针对response来判断是否需要重新定向或者重试
- 最后返回给RealCall
完整的流程图如下