本文基于okhttp3的3.11.0版本进行请求流程的分析:
implementation 'com.squareup.okhttp3:okhttp:3.11.0'

okhttp3的基本请求流程如上图所示,首先构建了网络请求所需的4个类(OkhttpClient,OkhttpClient.Builder,Request,Call),然后通过call.execute()或者call.enqueue(callback)来发起同步请求或者异步请求,下面我们来具体分析一下这两种方式如何进行网络请求的。
okhttp3发起请求的基本方式
1:创建OkhttpClient.Builder与OkhttpClient
OkHttpClient.Builder builder = new OkHttpClient().newBuilder()
.connectTimeout(20,TimeUnit.SECONDS)
.writeTimeout(20,TimeUnit.SECONDS)
.readTimeout(20,TimeUnit.SECONDS);
OkHttpClient okHttpClient = builder.build();
2:构建Request
Request request = new Request.Builder()
.url(url).build();
3:获取Call对象进行同步请求或者异步请求
Call call = okHttpClient.newCall(request);
1:call.execute();//同步请求
2:call.enqueue(callback);//异步请求 callback是okhttp3里面的Callback的实例对象
以上你okhttp3的简略请求步骤,下面我们通过跟踪源码的方式来具体分析一下请求的过程。
1:在获取Call对象是通过OkhttpClient里面的newCall方法进行构建的,newCall方法代码如下
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
在newCall方法内部只调用了RealCall.newRealCall(this, request, false /* for web socket */)方法,进入RealCall中的newRealCall()方法如下:
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
在这个方法内部,直接创建了一个RealCall对象,然后返回这个RealCall对象,而且RealCall是Call的子类(final class RealCall implements Call),它实现了Call类里面的方法,所以我们发送请求无论是调用execute()还是enqueue(),实际上都是调用了RealCall内部的方法。
//RealCall类内部的execute()方法
@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);//注释③
}
}
//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));//注释④
}
从RealCall类中的execute()与enqueue()方法中我们主要关注注释①②③④这四个点就基本上可以知道okhttp3.0网络请求的基本流程了,下面我们大概解释一下上面注释的方法。
(1)注释①中client.dispatcher().executed(this) 这里只是把同步请求的call加入了Dispatcher的任务队列管理中。
(2)注释②中是调用了okhttp中的拦截器链,经过一系列的操作,最终返回响应结果Response。
(3)注释③中是无论请求的结果如何,调用了Dispatcher的finished方法,将当前的请求移出队列。
(4)注释④是异步请求利用了Dispatcher的线程池来处理请求。
从RealCall类可以看出在RealCall内部的enqueue方法和execute方法中,都是通过OkHttpClient的任务调度器Dispatcher来完成。下面我们先来看看Dispatcher。
okhttp3的任务调度器-Dispatcher
通过client.dispatcher.execute(this)方法,我们点进去,可以看到在OkhttpClient类中的dispatcher()方法的具体内容如下:
public Dispatcher dispatcher() {
return dispatcher;
}
我们发现client.dispatcher()返回的是dispatcher对象,那么这个Dispatcher是何时创建的?我们查看OkhttpClient的构造方法如下:
public OkHttpClient() {
this(new Builder());
}
直接返回了一个new Builder(),我们继续跟踪代码,进入Builder()构造方法如下:
public Builder() {
dispatcher = new Dispatcher();
......
}
在Builder()方法中直接new 出来了一个Dispatcher对象,但是我们发现我们上面的client.dispatcher是Okhttpclient中的Dispatcher对象,我们这里跟踪到new 出来的Dispatcher对象是OkhttpClient中内部类Builder中的Dispatcher,我们继续看OkhttpClient的另外一个构造方法,如下:
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
......
}
在这个构造方法中把Builder作为参数传进来,然后赋值给OkhttpClient中的Dispatcher,然后这个构造方法什么时候调用的呢?在我们构建OkhttpClient的示例如下:
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
我们点进去在内部类中Builder中的build()方法,在这个build方法如下:
public OkHttpClient build() {
return new OkHttpClient(this);
}
在这个方法中直接返回了一个 new OkhttpClient(this)方法,这里的this就是当前的Builder类,也就是直接调用了OkHttpClient(Builder builder)构造方法,然后把Builder中的Dispatcher对象赋值给Okhttp中的Dispatcher。
现在我们弄清楚了OkhttpClient中的Dispatcher对象是怎么赋值的,然后我们看看Dispatcher内部做了一些什么操作。
首先我们要知道Dispatcher中一些重要的变量,如下注释所示:
//最大并发请求数
private int maxRequests = 64;
//每个主机的最大请求数
private int maxRequestsPerHost = 5;
//这个是线程池,采用懒加载的模式,在第一次请求的时候才会初始化
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<>();
我们再看看Dispatcher类中的构造方法如下所示:
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
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;
}
(1)Dispatcher中的默认构造方法是个空实现,线程池的加载方式采用的是懒加载,也就是在异步请求第一次调用的时候初始化,而我们上面构建Dispatcher实例就是调用了public Dispatcher() {} 这个构造方法。
(2)Dispatcher采用的线程池是无边界限制的线程池也就是没有核心线程,非核心线程数很大,也就是说可以无限的创建线程,比较适合执行大量的耗时较少的任务,线程池处于闲置状态的时候,线程池中的线程都会被销毁,这个时候该线程池几乎是不占用任何系统资源的。
下面我们分析一下ThreadPoolExecutor线程池中的参数的具体含义。
1、0:核心线程数量,保持在线程池中的线程数量(即使已经空闲),为0代表线程空闲后不会保留,等待一段时间后停止。
2、Integer.MAX_VALUE:表示线程池可以容纳最大线程数量
3、TimeUnit.SECOND:当线程池中的线程数量大于核心线程时,空闲的线程就会等待60s才会被终止,如果小于,则会立刻停止。
4、new SynchronousQueue<Runnable>():线程等待队列。同步队列,按序排队,先来先服务
5、Util.threadFactory("OkHttp Dispatcher", false):线程工厂,直接创建一个名为OkHttp Dispatcher的非守护线程。
(1)SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素,使用SynchronousQueue阻塞队列一般要求maximumPoolSizes为无界(Integer.MAX_VALUE),避免线程拒绝执行操作。
(2)在OKHttp中,创建了一个阀值是Integer.MAX_VALUE的线程池,它不保留任何最小线程,随时创建更多的线程数,而且如果线程空闲后,只能多活60秒。所以也就说如果收到10个并发请求,线程池会创建10个线程,当完成后的60秒后会自动关闭所有10个线程。他这样设计成不设上限的线程,以保证I/O任务中高阻塞低占用的过程,不会长时间卡在阻塞上。
下面我们来看看okhttp中的同步请求或者异步请求流程具体的实现细节。
同步请求流程分析
同步请求发起方式,okHttpClient.newCall(request).execute(),我们点进去execute()方法,我们就进入到了RealCall中的execute方法(具体为什么会是进入RealCall中的execute()方法,该文章前面有分析)如下:
@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);//注释④
}
}
如上所示我们具体分析一下上面打了注释的代码,在注释①中它主要是判断这个Call是否被执行过,可以看出每一个Call对象是只能使用一次,
现在我们来看看注释② client.dispatcher().executed(this);点进去如下,
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
它这里只执行Dispacther的executed方法,将任务添加到同步任务队列中,没有使用Dispatcher中的线程池。我们看注释③,它执行了getResponseWithInterceptorChain()方法,进入拦截器链流程(具体流程下一篇分析),然后进行请求,获取Response,并且返回Response。到最后我们来看注释④,它调用了client.dispatcher().finished(this),我们点进去Dispatcher中的finished方法中,如下:
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
在这个方法中直接调用了 finished(runningSyncCalls, call, false);方法,我们点进去看,如下:
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();
}
}
在注释①中从正在运行的队列(runningSyncCalls)直接移除了本次执行的call。至此,同步请求操作分析完。
同步请求总结
综合流程分析,同步请求只是利用了Dispatcher的任务队列管理,没有利用Dispatcher的线程池,然后通过拦截器链进行请求操作返回响应结果,最后不管结果如何都会调用Dispather里的finished方法,对于本次请求进行移除操作。
异步请求流程分析
异步请求发起方式,okHttpClient.newCall(request).enqueue(callback),我们进入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));//注释②
}
注释①中它主要是判断这个Call是否被执行过,可以看出每一个Call对象是只能使用一次。
注释②中,我们点进Dispatcher中的enqueue方法如下:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
(1)从上面代码中可以看出,如果当前正在执行的call的数量小于maxRequests(64),或者该call的Host上的call小于maxRequestsPerHos(5),则加入runningAsyncCalls队列并执行。反之,加入readyAsyncCalls队列排队等待。
(2)在这个方法中,我们可以看到, executorService().execute(call);就是把传进来的call对象加入了线程池中进行执行,当我们在线程池中任务执行,我们的结果肯定会回调到Call接口的具体实现类中,但是在Dispatcher这个类中我们没有看到call的具体实现类,我们重新再看创建一个异步请求的代码,client.dispatcher().enqueue(new AsyncCall(responseCallback));原来以参数传进Dispatcher类中的enqueue方法中的call是直接new AsyncCall,那么现在逻辑很明了了,当在线程池执行任务,我们获取的结果就会回调到AsyncCall(RealCall的内部类)的execute()方法中,为什么会回调在AsyncCall的execute()方法中呢?我们来看一下AsyncCall中是如何操作的,如下所示:
final class RealCall implements Call {
......省略代码
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
......省略代码
@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);
}
}
}
......省略代码
}
如上所示,我们可以看出:
(1)AsyncCall是RealCall类的内部类,并且继承了NamedRunnable。
(2)在AsyncCall中的构造方法中直接把传进来的responseCallback赋值给AsyncCall类中的变量Callback。
(3)实现了NamedRunnable中的execute()方法。
我们再点进去看一下NamedRunnable类中的具体逻辑,如下:
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();
}
(1)NamedRunnable是一个抽象类并且实现了Runnable。
(2)在NamedRunnable类中定义了一个抽象方法execute()在run()方法中调用,而execute()方法的具体逻辑就是在RealCall中的内部类AsyncCall中的execute()方法执行。所以在线程池中执行的任务最终回调会在AsyncCall类中的execute()方法中,下面我分析一下AsyncCall类中的execute()方法。
@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);//注释④
}
}
(1)注释①执行了getResponseWithInterceptorChain()方法,进入拦截器链流程(具体流程下一篇分析),然后进行请求,获取Response。
(2)注释②当次请求如果是canceled的状态,然后就会把这个状态回调到callback的具体实现类中的onFailure方法中。
(3)注释③把当次请求获取的response回调到callback的具体实现类中的onResponse方法中。
(4)注释④是移除当次请求的AsyncCall。
我们再进入当异步请求执行的Dispatcher类中的finished方法,看在异步请求下是怎么移除请求的,如下所示:
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
我们可以看到,在finished方法中的第三个参数是true异同与同步请求的false。我们再进去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();
}
}
在注释①中,是从正在运行的队列(runningSyncCalls)直接移除了本次执行的call,因为promoteCalls现在是true,所以执行了注释②中的promoteCalls()方法。我们点进去promoteCalls()方法,如下:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // 注释①
if (readyAsyncCalls.isEmpty()) return; //注释②
//注释③
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; // 注释④
}
}
(1)注释①如果此时并发的数量还是大于maxRequests=64,则直接返回。
(2)注释②如果此时没有等待的任务队列(readyAsyncCalls),则直接返回。
(3)注释③遍历等待队列(readyAsyncCalls)中的call,如果同一个host小于5个请求则从等待队列(readyAsyncCalls)中移除,然后加入运行的任务队列(runningAsyncCalls)中,最后线程池(executorService)执行execute()方法。
(4)注释④当正在运行的任务队列runningAsyncCalls>=64的时候,则返回。
异步请求总结
综合流程分析,异步请求利用了Dispatcher的线程池来处理请求,当我们发起一个异步请求时,首先会将我们的请求包装成一个AsyncCall,如果当前还能可以执行异步任务,则入队(runningAsyncCalls),并立即执行,否则加入等待队列(readyAsyncCalls)。当一个请求执行完毕后,会调用promoteCalls()方法,来把等待队列(readyAsyncCalls)中的AsncCall移出来,并加入到执行队列(runningAsyncCalls),并开始执行。然后在当前线程中去执行getResponseWithInterceptorChain(),直接获取当前返回的数据Response。
分析到这里,okhttp的请求流程就分析完毕了,下一篇,我们分析一下okhttp中的拦截器链是怎么运行。
本文深入分析了OkHttp3的3.11.0版本请求流程,包括同步和异步请求的创建及执行过程。通过构建OkhttpClient、Request和Call,详细解读了RealCall内部的execute()和enqueue()方法。同步请求利用Dispatcher任务队列管理,不涉及线程池;异步请求则通过Dispatcher的无界线程池处理,当请求完成会回调到AsyncCall的execute()方法。请求过程中涉及拦截器链和Dispatcher的重要角色。
877

被折叠的 条评论
为什么被折叠?



