OkHttp网络请求框架源码解析已经是一个老生长谈的问题了,很多公司在面试的时候也会问到,之前我都是看别人的解析流程,总感觉印象都不太深刻,所以我决定自己跟着源码走一遍,如有不对的地方还请大佬指出。话不多说,癞死狗!
网络请求一般有同步和异步两种,通常我们只会用到异步请求,发送请求时,首先调用newCall方法返回一个Call对象,然后调用Call对象的enqueue方法,就像下面的代码一样。之后的事情就都交给OkHttp去做了。那么OkHttp内部究竟是怎么做的呢?我们就从enqueue方法一步步深入。
Call call = mOkHttpClient.newCall(request);
call.enqueue(new CommonJsonCallBack(handle));
public interface Call extends Cloneable
查看一下Call的源码,发现它只是一个接口,而真正实现的是RealCall这个类,所以enqueue方法的具体实现自然就在RealCall中啦,进入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));
}
void execute(Runnable command);
RealCall.enqueue中最后一行,最终enqueue了一个AsyncCall对象,来看一下这个AsyncCall
这里建议呢不了解线程池的可以去先看看ThreadPoolExecutor的一些知识,免得跟我一样一脸懵x。
N天之后。。。。好啦,我看完Executor的相关知识啦。
从execute方法看到,它接收一个runnable对象,那我们回去看下OkHttp里线程池的execute方法传入的是谁,其实从一开始它就已经出现了,只是我们没有管它,回到RealCall的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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
这个NameRunnable就是一个带有线程名字的Runnable
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();
}
可以看到,在它的run方法里执行了execute方法,当然啦,它真正的实现是在AsyncCall中,主要进行一些状态的判断以及接口的回调,在finally中调用Dispatcher的finished方法结束请求,有一行比较巧妙
Response 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);
}
可以看到,它主要是添加了许多拦截器,最终形成了一个链,每个拦截器处理自己可以处理的事情,不能处理的就交给下一级去处理,这不就是典型的责任链模式嘛!我也是看了别人的分析才知道的,秒啊,妙啊!
回到AsyncCall的execute方法,接下来可能会有点绕,可能我对线程池用的太少了,这里理解的时候有点头晕,不过慢慢理一理还是可以搞明白的。
上面说到异步请求都是在线程池中执行的,我们通过Executor的exexute方法将AsyncCall对象发送到线程池中执行,而在Executor中会执行我们传进去的runnable的run方法,所以通过Dispatcher的enqueue方法,最终就会执行到AsyncCall的execute方法中,最后再调用Dispatcher的finished方法从而完成了一次请求,之后就是在UI线程中处理得到的response对象。
前面多次提到了Dispatcher,这个Dispatcher究竟是什么东东?接下来我们就来揭开它神秘的面纱,我们看一下它的成员变量就大致知道它是个什么东西了。
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** 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<>();
看!注释的明明白白的。其实啊,类如其名,它就是一个分发器,负责网络请求队列的请求任务状态的管理与转换,当然还有执行,不过执行是在线程池中做的,注意最后三个双端队列
readyAsyncCalls:已经准备好的异步请求,在线程池有资源的情况下此队列里的请求会"升级"到runningAsyncCalls队列中
runningAsyncCalls:正在执行的异步请求,如果线程池有资源则会执行此队列里的请求,否则会将请求添加到readyAsyncCalls队列中
runningSyncCalls :正在执行的同步请求,这个是同步请求才会用到的队列,原理都差不多,这里不在赘述。
回到 client.dispatcher().enqueue(new AsyncCall(responseCallback));
我们进到Dispatcher的enqueue方法中瞧一瞧
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
瞧!这就是那两个异步请求队列里数据的添加逻辑,和我上面说的差不多,最终的执行都是在线程池中执行的。有好事的小伙伴就要问了,你刚刚说的"升级"操作呢?这里看着它俩根本就没关系啊!没错,这里它俩确实没关系,但是,此处没关系不代表它俩一直没关系!前面说到AsyncCall的execute方法中无论如何都会调用finished方法,点进去看下
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
调用了私有的finished方法,再点进去看
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();
}
}
注意有一行if (promoteCalls) 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.
}
}
一目了然!首先会判断runningAsyncCalls中的请求是否大于最大请求数量,大于了肯定就没readyAsyncCalls的事了呗,重点是小于的时候的for循环,对readyAsyncCalls进行遍历,如果当前请求主机数小于最大请求主机数,就意味着可以"升级"啦,readyAsyncCalls就会把这个请求remove掉,当然它会加入到runningAsyncCalls中,之后就是在线程池中执行请求操作啦。这就又回到了AsyncCall的execute方法中,绕了一圈终于结束了,之后就在UI线程中处理得到的数据就行啦。