在本系列的上一篇文章中,我们学习了OkHttp的基本用法,体验了这个网络加载框架的强大功能,以及它非常简便的API。还没有看过上一篇文章的朋友,建议先去阅读 网络请求框架OkHttp3全解系列 - (一)OkHttp的基本使用 。
如果我们想要进行get请求,那么使用少量的代码就能实现,如下所示:
OkHttpClient httpClient = new OkHttpClient();
String url = "https://www.baidu.com/";
Request getRequest = new Request.Builder()
.url(url)
.get()
.build();
Call call = httpClient.newCall(getRequest);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
看起来很简洁的代码,实际是OkHttp在背后帮我们默默执行了成吨的工作。
知其然也要知其所以然。那么这篇我们就来解析一下OkHttp的源码,看看它在这些简单用法的背后,到底执行了多么复杂的工作。
请求的创建
以上面get请求的代码步骤分析,那么先分析OkHttpClient实例的创建。
在上一篇中,提到OkHttpClient实例化可以直接创建,也可以使用Builder建造者模式进行配置然后build()即可。直接创建其实就是使用了默认的配置。其构造方法如下:
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;//调度器,用于控制并发的请求。内部保存同步和异步请求的call,并使用线程池处理异步请求。
this.proxy = builder.proxy;//代理设置
this.protocols = builder.protocols;//http协议
this.connectionSpecs = builder.connectionSpecs;//连接配置
this.interceptors = Util.immutableList(builder.interceptors);//全局拦截器
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);//网络拦截器
this.eventListenerFactory = builder.eventListenerFactory;//请求的监听器的创建工厂
this.proxySelector = builder.proxySelector;//代理选择器
this.cookieJar = builder.cookieJar;//cookie,默认没有cookie:CookieJar.NO_COOKIES
this.cache = builder.cache;//网络缓存设置
this.internalCache = builder.internalCache;//内部使用的缓存接口
this.socketFactory = builder.socketFactory;//socket工厂
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;//以上几个是安全相关设置
this.connectionPool = builder.connectionPool;//连接池
this.dns = builder.dns;//域名解析系统
this.followSslRedirects = builder.followSslRedirects;//ssl重定向
this.followRedirects = builder.followRedirects;//重定向
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;//连接失败时是否重试
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;//几个超时设置
this.pingInterval = builder.pingInterval;//ping间隔时间
//拦截器不能是null
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
public static final class Builder {
...
public Builder() {
//默认的配置
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
...
}
直接创建OkHttpClient实例,配置项就是Builder构造方法中默认值。 可见配置项是非常多的,包括上一篇中已经使用过的超时设置、拦截器。
接着看Request的创建,也是使用建造者模式:
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Builder get() {
return method("GET", null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder method(String method, @Nullable RequestBody body) {
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if (body != null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
注意到get()和post(RequestBody body)都是对method方法的包装,method方法内部对请求方式和请求体进行了校验,比如get请求不能有请求体、post请求必须要请求体等。其他比较好理解,不再赘述。
接着看HttpClient的newCall方法:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
跟进RealCall的newRealCall方法:
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call);
return call;
}
可见HttpClient的newCall方法获得Call实际是RealCall。RealCall就是准备执行的请求,是对接口Call的实现。其内部持有OkHttpClient实例、Request实例。并且这里还创建了Transmitter给RealCall的transmitter赋值。
Transmitter意为发射器,是应用层和网络层的桥梁,在进行 连接、真正发出请求和读取响应中起到很重要的作用,看下构造方法:
public Transmitter(OkHttpClient client, Call call) {
this.client = client;
this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
this.call = call;
this.eventListener = client.eventListenerFactory().create(call);
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
Transmitter内部持有OkHttpClient、连接池、call、事件监听器。
再回头看RealCall实现的接口Call:
// 已准备要执行的请求。由于表示单个请求/响应对(流),因此无法执行两次
public interface Call extends Cloneable {
...
//同步请求,会阻塞
Response execute() throws IOException;
//异步请求
void enqueue(Callback responseCallback);
//取消请求,已经完成的不能取消。
void cancel();
boolean isExecuted();
boolean isCanceled();
Timeout timeout();
...
interface Factory {
Call newCall(Request request);
}
}
主要是定义请求的执行动作和状态。RealCall对Call的具体实现,在后面执行流程中说明。
好了,请求的创建就到这里了。
请求的调度
执行分为同步和异步,这里先从 同步请求 开始分析,即RealCall的execute方法:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();//超时计时开始
transmitter.callStart();//回调请求监听器的请求开始
try {
client.dispatcher().executed(this);//放入队列
return getResponseWithInterceptorChain();//执行请求获取结果
} finally {
client.dispatcher().finished(this);//请求结束
}
}
首先判断 如果已经执行,就会抛出异常。这就是一个请求只能执行一次的原因。然后回调请求监听器的请求开始。然后调用client的调度器Dispatcher的executed方法:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
很简单,请求放入一个双端队列runningSyncCalls中,表示正在执行的同步请求。
然后返回了getResponseWithInterceptorChain()的结果Response,可以猜到,同步请求真正的请求流程是在getResponseWithInterceptorChain方法中。
最后请求结束,会走Dispatcher的finished(Deque<T> calls, T call)方法,:
//结束 异步请求
void finished(AsyncCall call) {
//callsPerHost-1
call.callsPerHost().decrementAndGet();
finished(runningAsyncCalls, call);
}
//结束 同步请求
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
//异步、同步的结束,都会走到这里:从running中移除 并 调用promoteAndExecute
private <T> void finished(Deque<T> calls, T call) {
...
synchronized (this) {
//从队列中移除
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
...
}
从队列中移除call,然后执行了 promoteAndExecute(),这里先不跟进去了后面会讲到。
到这里,我们知道了,同步请求走的是getResponseWithInterceptorChain()方法;
我们再来看 异步请求,即RealCall的enqueue方法:
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();//回调请求监听器的请求开始
client.dispatcher().enqueue(new AsyncCall(responseCallback));//请求调度
}
同样先判断是否已请求过,回调请求开始。然后调用Dispatcher的enqueue方法,参数接受的是AsyncCall,AsyncCall继承NamedRunnable,NamedRunnable实现自Runnable,即AsyncCall就是个Runnable,可以想到它是会在线程或线程池执行run方法的。run方法在AsyncCall没看到啊,实际是在在NamedRunnable中:
//知道当前线程名字的Runnable
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
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(),execute()在AsyncCall是有实现的,这里先不看。
我们继续去看Dispatcher的enqueue方法:
void enqueue(AsyncCall call) {
synchronized (this) {
//存入等待执行的队列
readyAsyncCalls.add(call);
// 相同host的请求,共用一个 调用计数
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
//从 runningAsyncCalls或者readyAsyncCalls中找到相同host的请求
private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
先把请求放入双端队列readyAsyncCalls中,表示等待执行的异步请求。为啥是等待执行呢?先留一个疑问。 接着从 正在执行的请求runningAsyncCalls 或 等待执行的请求readyAsyncCalls 中找到是相同host的请求,把callsPerHost重用给当前请求。callsPerHost看名字感觉像是 拥有相同host的请求的数量,并且注意到类型是AtomicInteger,声明如下:
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
所以,相同host的请求是共享callsPerHost的,为了后面判断host并发做准备。
继续看,接着调用了promoteAndExecute(),前面看的finish方法也有调用,这里可以跟进看看了:
//调度的核心方法:在 控制异步并发 的策略基础上,使用线程池 执行异步请求
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();
if (runningAsyncCalls.size() >= maxRequests) break; //最大并发数64
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; //Host最大并发数5
i.remove();//从等待队列中移除
asyncCall.callsPerHost().incrementAndGet();//Host并发数+1
executableCalls.add(asyncCall);//加入 可执行请求 的集合
runningAsyncCalls.add(asyncCall);//加入 正在执行的异步请求队列
}
isRunning = runningCallsCount() > 0;//正在执行的异步/同步 请求数 >0
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());//可执行的请求
}
return isRunning;
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
遍历readyAsyncCalls,先进行两个检查:正在执行的异步请求runningAsyncCalls数量大于最大并发请求数64就break,或者 相同host请求的数量大于5,就continue。如果检查都通过,就从等待队列中移除,callsPerHost自增1,放入 可执行的集合executableCalls,并添加到队列runningAsyncCalls中,表示正在执行的异步请求。
这里就解释了 异步请求等待队列的意义了,就是为了控制最大并发数的缓冲:异步请求并发数达到64、相同host的异步请求达到5,都要放入等待队列。
遍历完后 把executableCalls中的请求都走executeOn方法:
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
executorService.execute(this);//在线程池执行asyncCall
success = true;
} catch (RejectedExecutionException e) {
...
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);//回调失败
} finally {
if (!success) {
client.dispatcher().finished(this); //执行发生异常,结束
}
}
}
//线程池的定义
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
executeOn方法很简单:使用类似CachedThreadPool的线程池 执行请求RealCall。如果执行失败,也会调用dispatcher的finished(Deque<T> calls, T call)方法。
前面分析过,AsyncCall的run方法会走到execute()方法,来看下:
protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();//超时计时开始
try {
Response response = getResponseWithInterceptorChain();////执行请求获取结果
responseCallback.onResponse(RealCall.this, response);//回调结果
} catch (IOException e) {
...
responseCallback.onFailure(RealCall.this, canceledException);//回调失败
...
} finally {
client.dispatcher().finished(this);//请求结束
}
}
我们发现,这里和 同步请求 就很像了,同样是调用getResponseWithInterceptorChain()方法来获取结果Response,不同点是使用responseCallback把结果回调出去,最后请求结束也是调用了dispatcher的finish方法。
另外,前面说过,finish方法中也调用了promoteAndExecute()方法,说明 同步/异步 请求 结束后 也会重新调度当前的异步请求。
好了,到这里我们把 调度流程 梳理下:
- 发起 同步 请求后,RealCall使用Dispatcher存入runningSyncCalls,然后使用getResponseWithInterceptorChain()获取结果,最后调用Dispatcher的finish方法结束请求。
- 发起 异步 请求后,RealCall使用Dispatcher存入readyAsyncCalls,获得host并发数,使用promoteAndExecute()方法 在 控制异步并发 的策略基础上,使用 线程池 执行异步请求(并发控制有包括 最大并发数64、host最大并发数5)。异步请求的执行 也是使用getResponseWithInterceptorChain(),获得结果后回调出去。最后调用Dispatcher的finish方法结束请求。
- Dispatcher:调度器,主要是异步请求的并发控制、把异步请求放入线程池执行,实现方法是promoteAndExecute()。 promoteAndExecute()有两处调用:添加异步请求时、同步/异步请求 结束时。
请求的执行
重点来啦!
通过上面分析指导,无论同步还是异步请求,最终的执行都是在RealCall的getResponseWithInterceptorChain()方法,只不过异步请求 需要先通过Dispatcher进行并发控制和线程池处理。那么就来看看getResponseWithInterceptorChain():
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); //使用者配置的 应用拦截器,最先拦截
interceptors.add(new RetryAndFollowUpInterceptor(client));//重试跟进拦截器
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);//链 开始执行
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
首先是 把
- 应用拦截器(外部配置)client.interceptors()、
- 重试跟进拦截器RetryAndFollowUpInterceptor、
- 桥拦截器BridgeInterceptor、
- 缓存拦截器CacheInterceptor、
- 连接拦截器ConnectInterceptor、
- 网络拦截器(外部配置)client.networkInterceptors()、
- 请求服务拦截器CallServerInterceptor,
依次 添加到集合interceptors中。然后使用interceptors、transmitter、originalRequest等创建了拦截器链RealInterceptorChain实例,最后用proceed方法获取到请求的结果Response。
在上一篇 使用方法中有提到拦截器Interceptor,那里配置的拦截器 实际就是 应用拦截器:client.interceptors(),是最早被添加到interceptors中。那么到底 拦截器是个啥呢?chain.proceed是如何获取到结果的呢?不着急,我们先看看Interceptor类:
//拦截器
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
//拦截器链
interface Chain {
Request request();
//Chain的核心方法
Response proceed(Request request) throws IOException;
//返回请求执行的 连接. 仅网络拦截器可用; 应用拦截器就是null.
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
Interceptor是个接口类,只有一个intercept方法,参数是Chain对象。再注意到 内部接口类Chain -- 拦截器链,有个proceed方法,参数是Request对象,返回值是Response,那么这个方法的实现就是请求的处理过程了。Chain的唯一实现类就是RealInterceptorChain,负责把所有拦截器串联起来,proceed方法就是串联的操作。
上述一系列的拦截器都是Interceptor的实现类,这里先贴出上一篇中实现的应用拦截器(其他拦截器的实现暂不去跟进):
new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
Response response = chain.proceed(request);
ResponseBody body = response.body();
Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
return response;
}
}
在intercept方法中我们调用chain.proceed方法获取了结果 并在前后打印了一些日志,那这个Chain实例是哪来的呢?intercept方法啥时候被调用的呢?— — 我们再回头看getResponseWithInterceptorChain方法,所有拦截器都被传入RealInterceptorChain,可以猜想到,必定是RealInterceptorChain的proceed方法内部调用了拦截器的intercept方法。 那么就来看看吧:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
在实例化RealInterceptorChain时 index赋值是0,exchange是null,所以前面三个if都没走进去。然后获取了第一个拦截器,也就是我们配置的应用拦截器,调用了它的interceptor方法,并返回和校验了结果。这里证实了我们猜想。同时注意到,调用 应用拦截器的interceptor方法传入的参数:拦截器链实例next,next就是把index + 1而已,其他参数和当前实例是一样的。也就是说 在我们的应用拦截器中调用的是 next的proceed方法。
进一步,next的proceed方法中 同样会获取interceptors的index=1的拦截器,即RetryAndFollowUpInterceptor实例,然后调用其interceptor方法,参数是index+1即index=2的chain。跟进RetryAndFollowUpInterceptor的代码发现,interceptor方法内部也是有调用chain的proceed方法。这样就会依次传递下去,直到最后一个拦截器CallServerInterceptor。
实际上 除了最后一个拦截器CallServerInterceptor之外,所有拦截器的interceptor方法都调用了 传入 chain的proceed方法。每个拦截器在chain的proceed方法 前后 处理了自己负责的工作。例如我们的应用拦截器,在chain的proceed方法前 打印了request信息的日志,chain的proceed方法获取结果 之后 打印了response信息的日志。每个拦截器interceptor方法在 调用chain的proceed方法时 都是为了获取下一个拦截器处理的response,然后返回给上一个拦截器。
逻辑总结如下图:
在这里插入图片描述
这就是 okhttp执行流程的核心了,整体流程如下:
在这里插入图片描述
现在来总结下:
- 拦截器链:把原始请求 request 依次 传入到 每个拦截器。拦截器 处理后 把response 反向 依次 回传。
- 拦截器:可以对request进行处理,然后调用index+1的拦截器链proceed方法 获取下一个拦截器处理的结果,接着自己也可以处理这个结果,即: 处理request、chain.proceed、处理response。
不知你有没有发现,这一过程 和 公司工作生产流程 很像:
- 老板接到一笔订单,要求10天内生产100台电脑。
- 总经理拿到任务后,修改了任务和时间:8天内生产110台,这是基于 生产合格率 以及进行重工、检验、包装、运输的时间上的考量,既要保质保量,也要按时交货。
- 任务接着到了部门经理,部门经理先去确认了仓库中是否有足够存货,如果有就直接使用存货来交货,这样不存在任何交货风险(质量、时间);如果没有存货,那么就去要求生产线生产。
- 生产线按时按量生产完以后,会把生产情况 上报给部门经理,部门经理把结果总结成excel呈现给总经理,总经理则会把整个生产流程结果及各部门的配合情况,总结成PPT报告给老板。
而不同的拦截器,在网络请求这一任务中,就扮演着不同的角色。可能okhttp的作者写拦截器的灵感就来源于生活吧,哈哈。
拦截器 | 作用 |
---|---|
应用拦截器 | 处理原始请求和最终的响应:可以添加自定义header、通用参数、参数加密、网关接入等等。 |
RetryAndFollowUpInterceptor | 处理错误重试和重定向 |
BridgeInterceptor | 应用层和网络层的桥接拦截器,主要工作是为请求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要进行解压。 |
CacheInterceptor | 缓存拦截器,获取缓存、更新缓存。如果命中缓存则不会发起网络请求。 |
ConnectInterceptor | 连接拦截器,内部会维护一个连接池,负责连接复用、创建连接(三次握手等等)、释放连接以及创建连接上的socket流。 |
网络拦截器 | 用户自定义拦截器,通常用于监控网络层的数据传输。 |
CallServerInterceptor | 请求拦截器,在前置准备工作完成后,真正发起网络请求,进行IO读写。 |
这里先大概知道每个拦截器的角色任务,下一篇将会详细分析每个拦截器,以及重要知识点--缓存和连接池。
那么,我们对okhttp执行流程的源码分析,到这里也结束了。
总结
现在通过两篇文章,我们已经掌握了okhttp的基本用法,并且通过阅读源码了解了okhttp总的执行流程——请求的创建、调度、拦截器链处理。接下来的文章,会深入到每个拦截器的具体实现,学习okhttp更多的高级使用技巧,感兴趣的朋友请继续关注。
欢迎 关注我的 公.众.号:胡飞洋
image
网友评论