一、概述
在日常开发中最常用的请求框架就是Okhttp了,本文将对okhttp的请求流程 由浅入深进行分析,由于我项目中使用的Okhttp版本为3.12.0,所以我们根据3.12.0的源码进行分析。
Okhttp特点:
OkHttp 是一个默认高效的 HTTP 客户端:
- HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
- 连接池减少了请求延迟(如果 HTTP/2 不可用)。
- 透明 GZIP 缩小了下载大小。
- 响应缓存完全避免了网络重复请求。
二、源码分析
在源码分析之前,我们先看一下Okhttp的简单使用。
//创建一个OkhttpClient
OkHttpClient client = new OkHttpClient();
//创建Request
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
//同步请求
Response response = client.newCall(request).execute();
这样就很简单的实现了一个请求,我们一点一点来分析,先来看OkhttpClient
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
//省略部分代码
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
//省略部分代码
}
可以看到在创建OkHttpClient时,使用的是建造者模式,最终创建出OkhttpClient,Builder的代码太长这里就不贴了,就是对一些配置的初始化,例如超时时间等,另外我们可以通过这个Builder.build()方法来构建出OkhttpClient。
接下来我们再来看Request是什么:
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
private volatile @Nullable CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags);
}
//省略部分代码
}
Request包含的就是请求的url,method,header,RequestBody等信息。同样是通过Builder来构建。
最后就是执行请求了,到这里之后就是重点了,好好看哦,
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
同样我们按照顺序,先来看client的newCall方法做了什么:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
newCall方法返回值为Call,返回了一个RealCall(Call的实现类)。
既然newCall返回的是RealCall,那么enqueue方法就一定再RealCall里面,就是说我们的同步/异步请求最终都是通过这个RealCall来执行的。所以我们来看RealCall的enqueue()方法:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
//如果为true 直接抛出异常,说明这个请求已经执行了
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
//事件监听回调
eventListener.callStart(this);
//重点
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
上面的最后一行代码又调用了client.dispatcher().enqueue()方法,就是Dispatcher类的enqueue(),我们来点进去看:
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
enqueue方法又将call加入了readyAsyncCalls,我们先来看看这是啥:
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
在Dispatcher类中有三个双端队列:
readyAsyncCalls:准备队列,
runningAsyncCalls:运行时异步队列
runningSyncCalls:运行时同步队列
所以说上面的enqueue方法中,就是将call先放到了准备队列,然后调用了promoteAndExecute()方法,我们来看看这个方法:
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
//用于保存要执行请求的队列
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//如果运行中队列大于maxRequests 64 终止执行
//就是正在请求的数量 不能大于64
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
//如果相同host同时请求大于maxRequestsPerHost 结束本次循环
//就是对同一个主机的同时请求 不能大于5
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
//符合条件时,将请求从准备队列中移除
i.remove();
//将请求加到执行队列
executableCalls.add(asyncCall);
//将请求加到运行中队列
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//遍历执行队列
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
//执行请求
asyncCall.executeOn(executorService());
}
return isRunning;
}
promoteAndExecute()方法的主要操作就是将符合执行条件的请求执行,最终调用的是asyncCall.executeOn(executorService())方法,下面我们先来看executorService()方法:
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
这个方法其实就是返回了一个线程池,所以说异步请求是通过线程池来处理的,我们再返回去看asyncCall.executeOn()方法:
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
//通过线程池 执行请求 this = RealCall
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
//异常处理
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
//
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
在上面的方法中,调用了线程池的execute(this)方法,就相当于调用了RealCall的execute,继续来看这个方法:
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
//这里最终通过拦截器 来拿到response
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) {
e = timeoutExit(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 {
//最终调用了finished
client.dispatcher().finished(this);
}
}
RealCall的execut方法就是通过拦截器来拿到response,最终调用了client.dispatcher().finished(this)方法,拦截器我们先放在后面讲,先继续看client.dispatcher().finished(this):
//最终调用的这个方法
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
//这里是重点 这里又调用了promoteAndExecute()
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
到这里是不是有一点豁然开朗了?这个方法里又调用了promoteAndExecute()方法,还记得刚开始的enqueue里面也是执行了这个方法嘛?所以说当一个请求结束后,又再次处理队列中的其他请求。
到这里整个请求的流程就结束了。我们继续来看一下上面提到的拦截器;
Response response = getResponseWithInterceptorChain();
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//声明一个list 存储拦截器
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));
//自定义的的network拦截器
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中有五种拦截器:重试拦截器,桥接拦截器,缓存拦截器,连接拦截器,请求服务拦截器,另外还有用户自定义的拦截器。其中拦截器是责任链模式,通过层层调用最终返回我们想要的Response,拦截器暂时不深入介绍了,后面有时间再研究吧。
异步请求看完之后,接下来就剩同步请求了,其实同步请求更简单,
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
同步请求则直接将请求通过dispatcher加入了运行中同步队列中,其他处理与异步请求大致相同,这里就不做太多介绍了。
三、总结
至此我们已经分析完了Okhttp请求的处理流程,我们最后总结一下:
- 创建OkhttpClient
- 构建Request请求
- newCall得到RealCall
- 通过RealCall调用enqueue/execute
- 通过dispatcher进行任务分发
- 最终通过拦截器得到Response
最后画一张流程图仅供参考:
img.jpg
网友评论