前言:被一位阿里面试官问到过这个问题,“让我谈一下okhhtp拦截器的调用顺序及影响。”
我脑子里只有责任链的设计模式,但是几种拦截器的顺序我确实没记忆过,有点尴尬。
所以呢,就来稍微研究一下,顺便记忆一下,正所谓“面试造航母,工作拧螺丝。”
复制代码
1)基本用法
此次我看的源码是v3.9.0, 首先我们来看一下官网上基本用法(get请求为例),如下
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
String jsonStr=response.body().string()
复制代码
可以看到主要分四步:1.构建OkHttpClient对象;2.构建Request对象;3.调用client.newCall(request)得到Call对象;4.Call对象执行同步方法execute(),或者异步方法enqueue(Callback responseCallback),得到响应的内容.
2)请求过程源码分析
2.1 OkhttpClient构建
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
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;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
...剔除部分源码
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;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
....
}
public static final class Builder {
Dispatcher dispatcher;
@Nullable Proxy proxy;
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
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;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
this.interceptors.addAll(okHttpClient.interceptors);
this.networkInterceptors.addAll(okHttpClient.networkInterceptors);
this.eventListenerFactory = okHttpClient.eventListenerFactory;
this.proxySelector = okHttpClient.proxySelector;
this.cookieJar = okHttpClient.cookieJar;
this.internalCache = okHttpClient.internalCache;
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.certificateChainCleaner = okHttpClient.certificateChainCleaner;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
this.authenticator = okHttpClient.authenticator;
this.connectionPool = okHttpClient.connectionPool;
this.dns = okHttpClient.dns;
this.followSslRedirects = okHttpClient.followSslRedirects;
this.followRedirects = okHttpClient.followRedirects;
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
this.writeTimeout = okHttpClient.writeTimeout;
this.pingInterval = okHttpClient.pingInterval;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
复制代码
可以看到Client对象是通过建造者模式设计的,直接new OkhttpClient()方式,传入默认的builder,或者通过配置builder,调用build()构建;
2.2 client.newCall(Request request
Request对象也是通过建造者模式构建的,根据不同的请求构建不同的Request对象;
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
/**
我们可以看到,client调用.newCall(request),实际调用的是RealCall.newRealCall(this, request, false),返回Call对象,说明Realcall应该是实现了Call接口;
在去看一下RealCall.newCall都干了些什么;
*/
final class RealCall implements Call {
...
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
//可以看到RealCall实现了Call接口,newRealCall 内部实际是将入参传入构造,构建了RealCall对象,创建一个call的事件监听;
在看看RealCall构造干了什么;
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
//内部将入参client,原始的请求赋值,同时构建了RetryAndFollowUpInterceptor()拦截器;
//该拦截器的作用就是发生错误时,进行重定向;
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
}
复制代码
2.3 call.excute() call.enqueue(callback)
我们继续跟进拿到call对象后,真正发起网络请求的操作 继续看call的实现类RealCall
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
//开始监听call请求
eventListener.callStart(this);
try {
//通过client的dispatcher,分发请求;Dispather中构建了一个线程池,所有的请求会加入到线程池中执行;
//这里
client.dispatcher().executed(this);
//这个方法很关键,也是Okhttp拦截器的责任链调用的开始,是这个库的精华所在,可以看到,在这里返回了服务器响应的内容
//我已经迫不及待一探究竟了,哈哈哈哈哈哈= =
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);
}
}
@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));
/**
*dispatcher对象的enqueue方法,可以看到,如果保存异步Call的集合小于最大请求数,并且未达到同一个host最大的连接数
*那么直接加入待执行队列,调用execute,执行;否则,先加入预执行队列
*/
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
}
复制代码
2.4 拦截器责任链的调用 getResponseWithInterceptorChain()
为了方便大家理解,请允许我先上个图:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
//1.构建一个空集合interceptors
List<Interceptor> interceptors = new ArrayList<>();
//2.可以看到首先会将client的interceptors全部加入这个集合头部,这个client的interceptors就是我们在构建Client Builder时,调用
// addInterceptor(Interceptor interceptor)加入的自定义拦截器,也就是所谓的应用拦截器;
interceptors.addAll(client.interceptors());
//3.尾部追加重定向拦截器 retryAndFollowUpInterceptor,还记得这个拦截器是什么时候创建的吗?
//没错,就是在new RealCall的时候构建的.
interceptors.add(retryAndFollowUpInterceptor);
//4.尾部添加BridgeInterceptor,设置默认的cookjar,这是一个请求和响应的连接器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//5.添加缓存拦截器,处理一些网络缓存逻辑,控制走网咯还是走缓存等
interceptors.add(new CacheInterceptor(client.internalCache()));
//6.添加网络连接拦截器
interceptors.add(new ConnectInterceptor(client));
//7.如果不是websocket,还添加了网络拦截器,添加自定义连接拦截器(比如我们常用的facebook调试框架的开源框架stecho)
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
//8.添加了请求服务拦截器
interceptors.add(new CallServerInterceptor(forWebSocket));
// 以上是存放在arryList中intercptor的顺序,
//9.可以看到接下来就是构建责任链Chain, 其实现类是RealInterceptorChain,
// RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
// HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
// EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout)
//10.其中,我们最主要来关注一下 index这个入参
//可以看到,链的开始index入参为0,我们继续跟进一下chain.proceed(originalRequest)干了什么;
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
/*==========================*/
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
public List<Interceptor> interceptors() {
return interceptors;
}
//进入Chain的实现类RealInterceptorChain,看一下proceed做了哪些事情
//我们看一些关键的地方.
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
//11.首先判断index是否超出拦截器索引,超出就跑异常,终止责任链
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.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.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 12.构建下一个chain对象,可以看到此处index+1,
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
//13.从集合中取出下一个拦截器,调用 interceptor.intercept(chain)返回Response;我们可以随便看一个interceptor的实现类,
//研究一下interceptor.intercept(chain) 是如何返回Response的;
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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;
}
复制代码
2.5 拦截器责任链的调用 interceptor.intercept(chain)
我们就以CacheInterceptor为例,看看它的intercept() fun都做了哪些事情.
@Override public Response intercept(Chain chain) throws IOException {
...省略部分源码,我们只看关键代码
//1.可以看到这里判断了网络请求是否存在,是否有响应缓存,两则都null,时,构建一个504错误码的Resonse并返回
// 该方法return
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
// 2.判断无请求网络,返回缓存内容,方法return
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
//3.上述两种情况都不满足,走到这里,调用chain.proceed(request)
//看到这个Api是否似曾相识呢~~哈哈哈哈哈,回到getResponseWithInterceptorChain()方法,其就是调用了
//chain.proceed(originalRequest)得到Response返回;
//而,chain.proceed 中如上分析,会把index++,构建下一chain,至此,责任链就完整形成了.
//前一个chain.proceed 返回值依赖后一个chain.proceed返回,由此递归,直到index 超出抛出超异常.
Response networkResponse = null;
try {
networkResponse = chain.proceed(networkRequest);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
}
}
....省略代码
return response;
}
复制代码
3)总结
我们可以看到Okhttp在构建Client和Request对象时用到了Builder模式,这才我们开发中也使用场景也很多,尤其是参数很多的情况下,通过构造或者set方法初始化就不是特别友好;Interceptor的责任链模式设计模式也很巧妙,我们可以通过很轻松的添加或删除拦截器实现许多业务场景,但是细想一下也不难发现,责任链模式如果链太长的情况下会发生什么?方法不断压栈,只有链调用结束或者异常时,才会出栈,释放栈空间. 有哪里写错或理解错误的欢迎讨论指出~~