系列
OKHttp3–详细使用及源码分析系列之初步介绍【一】
OKHttp3–流程分析 核心类介绍 同步异步请求源码分析【二】
OKHttp3–Dispatcher分发器源码解析【三】
OKHttp3–调用对象RealCall源码解析【四】
OKHttp3–拦截器链RealInterceptorChain源码解析【五】
OKHttp3–重试及重定向拦截器RetryAndFollowUpInterceptor源码解析【六】
OKHttp3–桥接拦截器BridgeInterceptor源码解析及相关http请求头字段解析【七】
OKHttp3–缓存拦截器CacheInterceptor源码解析【八】
OKHttp3-- HTTP缓存机制解析 缓存处理类Cache和缓存策略类CacheStrategy源码分析 【九】
Call
当我们封装好Request后需要执行这个请求,但是OKHttp并不是直接执行Request,而是将Request又封装了一层为Call对象,方便开发者对请求进行处理;一个Call对象代表一个已准备好执行的请求(Request),Call可以取消,同时一个Call对象代表了一个request/response 对(Stream),因此一个Call无法被执行两次
RealCall
Call只是一个接口,真正的实现类是RealCall,当我们执行同步请求时会调用它的execute方法,执行异步请求会调用enqueue方法,至于它内部是如何处理这些操作的,我们来通过源码进行分析
实例化RealCall
首先是通过OkHttpClient的newCall方法获取一个Call对象
Call call = httpClient.newCall(request);
@Override
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
这里只是new了一个RealCall对象,然后返回,进入到RealCall对象的构造方法看看
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
this.eventListener = eventListenerFactory.create(this);
}
这里可以看出RealCall对象会持有OkHttpClient对象,Request对象的引用,并且还实例化了拦截器链中的第一个拦截器RetryAndFollowUpInterceptor,也就是重试和重定向拦截器
重试这里好理解,请求失败了重新请求;那重定向什么意思呢?说到重定向一般会拿它跟请求转发进行比较
请求转发:
request.getRequestDispatcher().forward();
重定向:
response.sendRedirect();
例如:
请求转发:
request.getRequestDispatcher("/okhttp.jsp").forward(request,response);
重定向:
response.sendRedirect(request.getContextPath + "/okhttp.jsp")
-
转发过程:客户端首先发送一个请求到服务器,服务器匹配Servlet,并执行。当这个Servlet执行完后,它要调用getRequestDispacther()方法,把请求转发给指定的okhttp.jsp,整个流程都是在服务端完成的,而且是在同一个请求里面完成的,因此Servlet和jsp共享同一个request,在Servlet里面放的所有东西,在okhttp.jsp中都能取出来。因此,okhttp.jsp能把结果getAttribute()出来,getAttribute()出来后执行完把结果返回给客户端,整个过程只有一个请求,一个响应。
-
重定向过程:客户端发送一个请求到服务器端,服务器匹配Servlet,这都和请求转发一样。Servlet处理完之后调用了sendRedirect()这个方法,这个方法是response的方法。所以,当这个Servlet处理完后,即response.sendRedirect()方法执行完立即向客户端返回响应,响应行告诉客户端你必须再重新发送一个请求,去访问okhttp.jsp;紧接着客户端收到这个请求后,立刻发出一个新的请求,去请求okhttp.jsp,在这两个请求互不干扰、相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。因此,在sendRedirect()里面是两个请求,两个响应。
Forward是在服务器端的跳转,就是客户端发一个请求给服务器,服务器直接将请求相关参数的信息原封不动的传递到该服务器的其他jsp或Servlet去处理。而sendRedirect()是客户端的跳转,服务器会返回客户端一个响应报头和新的URL地址,原来的参数信息如果服务器没有特殊处理就不存在了,浏览器会访问新的URL所指向的Servlet或jsp,这可能不是原来服务器上的webService了
总结:
-
转发是在服务器端完成的,重定向是在客户端发生的
-
转发的速度快,重定向速度慢
-
转发是同一次请求,重定向是两次请求
-
转发时浏览器地址栏没有变化,重定向时地址栏有变化
-
转发必须是在同一台服务器下完成,重定向可以在不同的服务器下完成
同步请求
同步请求会走到内部的execute方法
@Override
public Response execute() throws IOException {
//第一步
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//第二步
captureCallStackTrace();
try {
//第三步
client.dispatcher().executed(this);
//第四步
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
//第五步
client.dispatcher().finished(this);
}
}
- 第一步:首先出现一个同步代码块,对当前对象加锁,通过一个标志位executed判断该对象的execute方法是否已经执行过,如果执行过就抛出异常;这也就是同一个Call只能执行一次的原因
- 第二步:这个是用来捕获okhttp的请求堆栈信息,不是重点
- 第三步:调用Dispatcher的executed方法,将请求放入分发器,这是非常重要的一步
- 第四步:通过拦截器连获取返回结果Response
- 第五步:调用dispatcher的finished方法,回收同步请求
第三步和第五步都在分发器内部处理,这里不再赘述,可以参考前面有关分发器的文章
我们看下第四步
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);
}
这里实例化一个List,用来存放Interceptor对象,主要是OKHttp提供的5种拦截器
接下来实例化了一个拦截器链对象,在这个拦截器链里依次执行每个拦截器
至于拦截器具体做了啥就放到后面的关于拦截器的文章中讲述
异步请求
异步请求会走到内部的enqueue方法
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
首先也是加同步,判断这个Call是否执行过;然后实例化了一个AsyncCall,最后调用分发器的enqueue方法
我们看下AsyncCall是什么
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
首先它是RealCall的内部类,我们都知道异步请求是在子线程执行的,但是到这里我们还没有看出来子线程的影子,那我们就需要看下RealCall的父类了
NamedRunnable实现了Runnable接口,从这里可以看出AsyncCall确实是在子线程执行网络请求
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
NamedRunnable并没有做过多的处理,但它提供了一个抽象方法execute,也就是让子类在这个方法中去执行耗时操作
回头看AsyncCall的execute方法,也有调用getResponseWithInterceptorChain这个方法,跟同步请求差不多,同时会将请求结果通过Callback返回回去,一定要注意,回调是在子线程执行的,不要直接在这里进行更新UI的操作
RealCall源码
/**
* 一个Call封装一对Request和Response,能且仅能被执行一次。并且Call可以被取消。
*/
final class RealCall implements Call {
//OKHttp客户端
final OkHttpClient client;
//重试和重定向拦截器
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
//RealCall状态监听器
final EventListener eventListener;
//请求对象
final Request originalRequest;
final boolean forWebSocket;
// RealCall是否执行过
private boolean executed;
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
this.eventListener = eventListenerFactory.create(this);
}
/** 返回初始化此Call的原始请求 */
@Override
public Request request() {
return originalRequest;
}
/** 进行同步请求
* 立即发出请求,一直阻塞当前线程,直到返回结果或者报错
* 可以使用Response.body获取结果
* 为了复用连接,需要调用Response.close关闭响应体
*/
@Override
public Response execute() throws IOException {
synchronized (this) {
//同一个RealCall,只能执行一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//打印堆栈信息
captureCallStackTrace();
try {
//加入分发器中的正在执行的同步请求队列
client.dispatcher().executed(this);
//获取请求结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
//通知分发器请求完成,从队列移除
client.dispatcher().finished(this);
}
}
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
/**
* 进行异步请求
* 至于何时执行这个请求由分发器决定
* 通常是立即执行,除非当前有任务在执行或不符合限制条件
* 如果不能立即执行,会被保存到等待执行的异步请求队列
* 请求结束后,会通过回调接口将结果返回
*/
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
//实例化一个线程AsyncCall交给分发器,由分发器中的线程池执行这个线程
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
//取消这个RealCall 如果请求已经有返回了,那么就不能被取消了
@Override
public void cancel() {
retryAndFollowUpInterceptor.cancel();
}
//判断是否执行过
@Override
public synchronized boolean isExecuted() {
return executed;
}
//判断是否取消了
@Override
public boolean isCanceled() {
return retryAndFollowUpInterceptor.isCanceled();
}
//复制一个相同的RealCall
@SuppressWarnings("CloneDoesntCallSuperClone")
@Override public RealCall clone() {
return new RealCall(client, originalRequest, forWebSocket);
}
//StreamAllocation协调Connections,Streams,Calls三者之间的关系
StreamAllocation streamAllocation() {
return retryAndFollowUpInterceptor.streamAllocation();
}
//异步请求线程
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
//主机名
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
//当分发器的线程池执行该对象时,该方法被调用
@Override
protected void execute() {
//保证onFailure只被回调一次
boolean signalledCallback = false;
try {
//通过拦截器获取返回结果
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
//如果请求被取消,回调onFailure
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
// 正常情况,调用onResponse
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
// 如果上面回调过,这里就不再进行回调,保证onFailure只会被调用一次
if (signalledCallback) {
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
//通知分发器请求结束,从队列中移除该请求
client.dispatcher().finished(this);
}
}
}
/**
* Returns a string that describes this call. Doesn't include a full URL as that might contain
* sensitive information.
*/
String toLoggableString() {
return (isCanceled() ? "canceled " : "")
+ (forWebSocket ? "web socket" : "call")
+ " to " + redactedUrl();
}
//返回包含此URL的字符串,无用户名,密码,查询信息
String redactedUrl() {
return originalRequest.url().redact();
}
//依次执行拦截器链中的拦截器获取结果
Response getResponseWithInterceptorChain() throws IOException {
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());
}
//添加连接服务器拦截器,主要负责将我们的Http请求写进网络的IO流中
interceptors.add(new CallServerInterceptor(forWebSocket));
//构建拦截器链依次执行每一个拦截器
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
}
总结
RealCall的代码并不复杂,也没有做过多的操作,而且同步异步的请求在RealCall中的操作基本差不多,只不过同步请求是在当前线程执行,而异步请求是封装了一个子线程AsyncCall去执行请求;而关于具体的网络请求过程都在拦截器里进行,这个后续再解析