业余时间把源码clone下来大致溜了一遍,并且也参阅了其余大神的博客,在这里把自己的心得记录下来共享之,如有不当的地方欢迎批评指正。本文是Okttp源码解析系列的第一篇,不会深入写太多的东西,本篇只是从代码流程上简单的做个梳理,算是为后面的系列博文做个铺垫。
网络请求其实笼统的说一句废话就是发送请求(Request)并接受响应(Response)的过程,当然内部细节在此处不深究
Okhttp完成一个网络请求无非也是这样,一行代码就可以概括(这里先分析同步调用):
Response response = new OkHttpClient().newCall(request).execute();
所以沿着这段代码的调用过程,抽丝剥茧,很容易得出Okhttp的执行流程,newCall()返回的是一个RealCall对象,该对象的execute()方法如下:
public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
public Response execute() throws IOException {
//省略部分与本文无关的代码
try {
//把当前的RealCall对象交给dispatcher保存
client.dispatcher().executed(this);
//调用getResponseWithInterceptorChain()来返回结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
很简单上面的两个方法也就做了如下几点功能
1)需要事先组织Request请求对象,该对象包含了请求的url,请求体等内容
2) 将Request对象交给Call对象(RealCall)
3) 执行RealCall对象的execute方法,该方最终返回服务器的响应内容Response对象。
该Response对象是通过getResponseWithInterceptorChain()方法返回而来的!下面就着重看看这个方法都做了些神马功能!
final Request originalRequest;//通过newCall方法传过来
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
//获取用户自定义的拦截器对象:也可以不设置
interceptors.addAll(client.interceptors());
//以下拦截器是Okhttp内置的拦截器
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));
//将拦截器集合交给RealInterceptorChain这个Chain对象来处理
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
//执行
return chain.proceed(originalRequest);
}
这个方法从代码上来看也很简单,就是将客户端自定义的拦截器(此处先不管Interceptor对象是来干什么的,先当做普通的Java对象类看待)和Okhttp内置的拦截器放到一个List集合interceptors里面,然后跟最初构建的Request对象一块创建了RealInterceptorChain对象,RealInterceptorChain是Chain接口的实现类。这样就把
一系列拦截器组合成一个链跟请求绑定起来,最终调用RealInterceptorChain的proceed来返回一个Response;
简单用图来表示一下此时这个链的关系,可以如下表示:
那么怎么样让 这些拦截器对象逐一运行呢?getResponseWithInterceptorChain方法的最后调用了RealInterceptorChain的proceed方法,该方法直接调用了该对象的重载方法:
//正式开始调用拦截器工作
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection){
//省略部分与本文无关的代码
// 调用链中的下一个拦截器
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
//确保每个拦截器都调用了proceed方法()
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
//省略部分与本文无关的代码
return response;
}
剔除了与本文无关紧要的代码之后,上面的代码意思也很明显,在总结上面的方法之前看看RealInterceptorChain的构造器是都做了什么初始化工作
//拦截器当前索引
private final int index;
private final Request request;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
//省略部分与本文无关的代码
this.index = index;//0
this.request = request;
}
通过其构造函数可以RealInterceptorChain有一个index变量获取拦截器列表中对应位置的拦截器对象,但是procced方法并没有用for循环来遍历interceptors集合,而是重新new 一个RealInterceptorChain对象,且新对象的index在原来RealInterceptorChain对象index之上进行index+1,并把新的拦截器链对象RealInterceptorChain交给当前拦截器Interceptor 的intercept方法:
//生成新的拦截器链对象
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
//开始执行拦截:将新的next对象交给interceptor处理
Response response = interceptor.intercept(next);
抛却自定义的拦截器对象先不谈,上面的表述可以用下图来表示:
既然Interceptor的intercept方法接受的是一个新RealInterceptorChain对象,通过上面的代码来看目前index只能达到index=1,那么剩下的CacheIntercept对象是如何调用的呢?其实也可以大胆的猜测一下:
既然拦截器组成了一个链,那么肯定是 第一个内置拦截器RetryAndFollowUpInterceptor对象接受的Chain对象在intercept方法里面继续对新的Chain做了遍历下一个拦截器的操作!
所以大致看下RetryAndFollowUpInterceptor对象的intercept方法都做了神马:
Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//省略部分与本文无关的代码
Response priorResponse = null;
while (true) {//死循环
//省略部分与本文无关的代码
//上一个拦截器返回的响应对象
Response response = null;
//省略部分与本文无关的代码
//继续执行下一个chain的下一个
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
//省略部分与本文无关的代码
Request followUp = followUpRequest(response);
if (followUp == null) {
//返回return
return response;
}
//省略部分与本文无关的代码
priorResponse = response;
}//end while
}
可以发现RetryAndFollowUpInterceptor对象的intercept有一个while(true)的循环,在循环里面有一行很重要的代码:
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
看!上面的也跟getResponseWithInterceptorChain()一样调用了proceed方法,根据上面的的讲解:调用Chain的proceed方法的时候会新生成一个RealInterceptorChain,其index的值是上一个拦截器所持有的Chain的index+1,这样就确保拦截器链逐条运行。查看BridgeInterceptor、CacheInterceptor等Okhttp内置拦截器就可以印证这一点:在它们intercept的内部都调用了chain.proceed()方法,且每次调用都在会创建一个RealInterceptorChain对象(当然最后一个拦截器CallServerInterceptor除外)!所以拦截器的工作流方式可以用如下图来表示:
如果从发起请求Request到得到响应Response的过程,加上拦截器链的话工作在内,其i整体执行过程就是如下了:
这也是为什么RealInterceptorChain的procced方法中有如下的异常抛出,目的就是为了让拦截器链一个个执行下去,确保整个请求过程的完成:
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
也即是说自定义的拦截器Interceptor必须调Chain的proceed一次(可多次调用),那么到此为止一个完 整的Okttp(同步)请求的流程就已经完成,那么就简单的用一个流程图来总结本文的说明吧: