OkHttp源码分析
分析版本:3.9.x
众所周知OkHttp请求有四步:
- 创建OkHttpClient对象
- 创建Request对象
- 通过newCall方法创建Call对象
- 通过Call对象的excute()方法发送同步请求,enqueue()方法发送异步请求
OkHttpClient
可以查看源码对于OkHttpClient的注释:
Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their
responses.
简而言之就是Call作为发起一个网络请求和接受服务器响应的存在,OkHttpClient则是Call的工厂类,查看Call这个类发现它是有个接口,实际上发送请求的是RealCall,这个后面会讲到.使用Okhttp最开始要做的就是创建一个OkHttpClient对象,源码提供了两种方法,一种直接new OkHttpClient(),另一种是通过OkHttpClient.Builder(),Builder是OkHttpClient的一个静态内部类,通过建造者模式链式调用来配置一些参数,如请求超时时间,读写超时时间以及各种Interceptor等,简单样式如下:
OkHttpClient client = new OkHttpClient
.Builder()
.addInterceptor(interceptor);
.retryOnConnectionFailure(true)
.callTimeout(20, TimeUnit.SECONDS)
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build();
而第一种直接new OkHttpClient()其实内部也是调用了Builder的构造方法,而在其无参构造方法中对各个参数做了默认配置.两种方式指向了同一个方法build()
public OkHttpClient build() {
return new OkHttpClient(this);
}
Request
先看一下Requst内部:
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
}
可以看到Request封装了url,请求头请求体等信息,内部同样有一个Builder类可以通过构造者模式来构建一个Request对象
Request request = new Request.Builder()
.url(url)
.get()
.build();
Call
要获取Call对象需要通过OkHttpClient的newCall方法
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
可以看到newCall方法内部返回的实际是Call接口的实现类RealCall
Call.excute()&enqueue()
获取到Call对象后就可以调用excute或enqueue方法来发送请求,首先我们看源码
同步请求excute:
@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);
}
}
可以发现我们最想要的response其实是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);
}
这里其实OkHttp使用责任链设计模式,将所有Interceptor添加到一个list中,做是否是WebSocket连接判断,然后将这个list作为参数构建RealInterceptorChain对象,最后调用其proceed()方法.
我们先暂时放下proceed方法,来了解一下什么是责任链模式:
- 责任链模式(Chain of Responsibility Pattern),有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求.但是发出者并不清楚到底哪个对象会处理该请求,所以,责任链模式可以实现在隐藏客户端的情况下,对系统进行动态的调整.
实际在OkHttp中采用的是不纯的责任链模式,我们知道纯责任链模式是直到某一对象决定处理该请求,针对不同的请求做不同的操作,类似if-else,而不纯责任链模式根据一个请求每个对象都会处理该请求的一部分,处理完成随机将请求给下一个对象
结合代码来看,我们点进proceed方法(省略部分代码)
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
calls++;
// 索引加一,创建调用下一个Interceptor的RealInterceptorChain对象.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//获取当前Interceptor对象
Interceptor interceptor = interceptors.get(index);
//intercept方法内部调用下一个chain对象的proceed方法形成责任链
Response response = interceptor.intercept(next);
return response;
}
可以看到proceed方法内部会获取当前index的Interceptor,调用intercept方法将同时创建的下一个Interceptor调用的chain对象作为参数传入,而在intercept方法内部除了对请求会做一些配置和处理外会继续调用传入chain对象的proceed方法,这样就使多个interceptor串联成了责任链,而在最后的CallServerInterceptor没有调用proceed方法,这样在它处理完成后response就会层层返回直到最开始的getResponseWithInterceptorChain()方法.这样整个请求完成我们就得到了最终的response.
现在我们来看下这整个Interceptor责任链的调用顺序及各个Interceptor的作用
- RetryAndFollowUpInterceptor:处理请求错误并根据需要进行重定向
- BridgeInterceptor:通过设置内容长度,编码,添加cookie,设置请求头,gzip压缩等来将客户端请求转化为服务器能读取的请求,再讲服务器返回的response转化为客户端能读取的response
- CacheInterceptor:处理缓存
- ConnectInterceptor:给目标打开一个链接
- CallServerInterceptor:最后一个拦截器,对服务器进行访问
异步请求enqueue:
@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));
}
看一看到方法内部调用了dispatcher().enqueue(new AsyncCall(responseCallback)),这里我们点进dispatcher类先看下他的几个变量:
public final class Dispatcher {
//最大并发请求数
private int maxRequests = 64;
//单个域名最大请求数
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** 内部线程池用于处理异步请求 */
private @Nullable ExecutorService executorService;
/** 就绪异步请求队列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 正在执行的异步请求队列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 正在执行的同步请求队列 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
}
其中runningSyncCalls我们在同步请求excute方法中其实看到有dispatcher.excute()和finish()方法,内部就是将同步请求添加到runningSyncCalls中进行管理,具体的请求及获取响应还是要走getResponseWithInterceptorChain(),所以我们关注点放在剩下两个请求队列和线程池,他们在源码中怎么使用我们接下来看enqueue方法:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
这里会判断最大请求数和最大单个域名请求数,如果超过就会加入到readyAsyncCalls,否则加入runningAsyncCalls,并调用线程池execute方法,这个线程池设置只有非核心线程,最大线程数为Integer.MAX_VALUE,超时时长60s,其任务队列相当于一个空集合,所以任何任务都会立即执行,看代码其实Dispatcher内部线程池和Java的CacheThreadPool一模一样.
回到excute方法,他传入了AsyncCall参数,AsyncCall是RealCall的内部类,继承NamedRunnable抽象类,而NamedRunnable又实现了Runnable接口,看代码:
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
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重写run方法内部调用了抽象方法execute,而AsyncCall又重写了execute方法,现在经过一系列包装异步请求走到了AsyncCall的execute方法,可以看到内部还是调用了getResponseWithInterceptorChain()来获取response.
文章参考:
https://my.oschina.net/wangzhenchao/blog/745471/
https://blog.csdn.net/longyulu/article/details/9159589/
https://www.jianshu.com/p/e6fccf55ca01/