下面放一下okhttp 的基本使用,在项目中 一般okhttpclient 都是单例使用, 测试代码如下
kotlin
fun test(){
var client=OkHttpClient.Builder()
.connectTimeout(5*1000,TimeUnit.SECONDS)
.addInterceptor {
chain -> chain?.proceed(chain.request())
}
.addNetworkInterceptor {
it.proceed(it.request())
}
.build()
var call=Request.Builder()
.url("http://www.baidu.com")
.get().build()
var realCall = client.newCall(call)
realCall.enqueue(object : Callback{
override fun onFailure(call: Call?, e: IOException?) {
}
override fun onResponse(call: Call?, response: Response?) {
}
})
}
这段代码中添加了两个拦截器 一个是 addInterceptor 另外一个是 addNetworkInterceptor , 这是一个比较重要逻辑,我们先记录一下
整体的分析从 client.newCall(call) 开始,下面开始上代码
typescript
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
在 newCall 的时候,创建了一个 RealCall ,这个RealCall 实现自 Call
csharp
public interface Call {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
interface Factory {
Call newCall(Request request);
}
}
Call 当中有两个比较重要的方法,一个是 execute ,另外一个是 enqueue 方法 ,其中 execute 是 同步请求方法, enqueue 是异步请求方法,由于 execute 逻辑简单,这里就分析一下RealCall.enqueue 这个方法
java
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
从方法中看到 这里使用了对象锁, 如果这个RealCall 已经被执行了,那么他不能再次被使用,然后在标记完成后使用使用 AsyncCall 包装一下 callback , 并且使用 client.dispatcher 调度 这个请求 ,下面继续分析 Dispatcher 的 enqueue 方法
scss
synchronized void enqueue(AsyncCall call) {
//todo 同时异步请求不能超过64个,同一个host 的请求不能超过5个
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
我在注释里面标记了这个方法的主要逻辑, 同时异步请求不能超过64个,同一个host 的请求不能超过5个,讲到这里就必须说一下 Dispatcher 中的任务队列
swift
/** 异步请求等待队列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 异步请求执行队列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 同步请求队列 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
在 Dispatcher 中一共存在 3 个队列, 2个是 异步队列 , 异步队列包括等待执行队列和正在执行的队列,当我们向 Dispatcher 中添加异步任务的时候, 他会判断上面的逻辑,如果超出那么将任务放入等待队列中,
如果满足上述条件,就会使用 线程池来调度任务, 这里的线程池创建的也是非常有意思
csharp
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;
}
上述线程中主要线程数 0 , 最大线程数是 Integer.MAX_VALUE ,同时创建了一个 队列长度是 0 的 SynchronousQueue , 想要弄明白这3个参数的实际意义就要知道 线程池的调用逻辑
当有任务需要交给线程池调度时,如果当前正在执行的线程数小于主要线程数,那么则会创建线程执行任务,当正在执行的线程数大于或等于主要线程数,任务需要先进入队列,等待任务队列满时,再判断正在执行线程数是否小于最大线程数,如果小于最大线程数,则创建线程执行任务,否则执行抛弃任务策略
了解了线程池的执行逻辑,再来看一下 okhttp 中的线程池参数下,他的逻辑是什么
由于他的主要线程数为0,同时队列的个数是0, 同时最大线程数是 Integer.MAX_VALUE ,当任务来临时 主要线程数不满足,执行添加队列逻辑,队列容量是0,执行最大线程数逻辑,最大线程数是 Integer.MAX_VALUE , 开启线程执行任务,这样即保证了任务的执行速度,同时也保证了任务执行完成后释放线程资源
线程池调度后就回到了 AsyncCall 这个Runnable 的 run 方法, AysncCall 继承自 NameRunnable ,run 方法会调用他的execute 方法
java
@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);
}
}
这个方法就开始了责任链调用, 但是我们先不看他调用的流程,我们先看 这个方法中的 finally 代码块 ,他调用了 Dispatcher 的 finished 方法
scss
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
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;
}
// 没有正在执行的任务,并且有 idleCallback ,则调用 idleCallback 执行闲时逻辑
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
这里 promoteCalls 就是检查是否有等待的异步任务,如果有那么则等待任务放入执行中的队列,没有任务则执行空闲逻辑
scss
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.
}
}
这个方法的逻辑还是判断最大请求数和同一host的请求数,如果满足条件,将等待任务放入执行中队列,
下面我们继续分析责任链 getResponseWithInterceptorChain 方法
csharp
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
到了这里就到了我们开篇所说的那个 Interceptor 逻辑, 我们调用的 addInterceptor 添加的 Interceptor 是放在所有的 Interceptor 之前, 而 addNetworkInterceptor 方法添加的 Interceptor 是请求前 最后的拦截器, 那么他们在请求执行过程中的逻辑就是 addInterceptor 添加的 Interceptor 先处理请求信息最后处理返回结果, addNetworkInterceptor添加的 Interceptor 只在 CallServerInterceptor 之前执行 处理请求信息而他处理结果是最先的,
到了这里请求与调度的逻辑就执行完毕了