因为公司的项目中使用了的Retrofit2+OK3,所以有时间闲下来还是有必要研究一下它的源码,本来就抱着试试看的心态,项目上线了,休息休息也是可以的,呵呵。所以就走马观花的去读了一遍,写下自己的分享,作为一个新人,在没有提前看别人的分析的情况下,我想我是看懂了一点点,大家不要见笑。基于OK3的源码啊。。
先来看个请求吧,普通的GET请求,服务器也是我自己搭建的(thinkPHP3.3环境,不要问我为什么会PHP,人都是鼻出来的!),来看代码:
Request.Builder builder = new Request.Builder().url(Api.GET_URL);
final Request request = builder.build();
OkHttpClient mOkHttpClient = new OkHttpClient() ;
Call mCall = mOkHttpClient.newCall(request);
mCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG , "onFailure : " + call );
Log.d(TAG , "onFailure exception : " + e) ;
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG , Thread.currentThread().getName() + "onResponse : " + call);
Log.d(TAG, Thread.currentThread().getName() + " the response is " + response.body().string()) ;
}
});
先看Request.Builder吧,这个我先说一下吧,Request英文名对应请求,那么它肯定就是ok3封装的请求数据,Builder见得多了,装13一点就是Builder模式,写过android的对话框的,AlertDialog.Builder跟它一个样,顺便截个图,看看Request.Builder中到底有什么:
是的,它有HttpUrl,method,Header.Builder,RequestBody,tag。我们就简单理解一下吧,分别就是请求的Url,method,header 和 请求的body吧,这个先不看,简单了解一下,我想想也是,哈哈。
再来看这句代码吧:
OkHttpClient mOkHttpClient = new OkHttpClient() ;
Call mCall = mOkHttpClient.newCall(request);
我们来看看newCall是干嘛用的:
它返回了一个newCall的东西,不管了,先提着裤子进去看看吧:
看不懂啊,先放着,接着看下面的代码:
mCall.enqueue(new callback);
此时的mCall就是我们已知的realCall,看看它的enqueue方法是做啥的:
信息量比较大,还是挑重点的吧,调用的了
client.dispatcher().enqueue(new AsyncCall(responseCallback));
这个client就是我们的OkHttpClient,那么这个dispatcher是什么呢?进去看看吧:
英文解释为异步的请求策略,看到下面的ExecutorService就是用线程池了,现在也不管最大并发数是多少了,自己进来看看就知道了。这个dispatcher应该就是OK3分发异步请求核心了,我们去看看它的enqueue方法了:
信息量也是比较大,我也就不展开扯了,判断正在执行的call数量和访问同一主机的数量,这些都不重要,重要的是看到了executorService().execute(call);多线程中学到了executorService.executte方法中,一定传入的是个Runnable对象,那么好吧,我们来看看这个AsyncCall对象是什么玩意吧:
看到了吧,的确是实现了Runnable接口的货,那个NamedRunnable是实现了Runnable接口,用意是要在Runnable在run时有个好用的名字,官方说为了避免每次设置名字而创建的:
看完了这,那就看看AsyncCall中的execute方法吧:
到现在为止,我们找到了在开始请求时,设置的Callback,在这里已经看到原型了,非常开心,非常高兴了,感觉马上就需要完成了啊。
我们的Response来自getResponseWithInterceptorChain()方法,进去看看了,看完了马上就可以睡觉了:
来看看这个图,为了这个方法我硬是傻了很长一段时间,设计了一个拦截器容器,装入了client.Interceptor,失败和重定向的Interceptor,请求头的Interceptor,还有缓存的Interceptor,连接用的Interceptor,还有netWork的Interceptor,最后一个连接服务器的Interceptor。
然后让我们的RealInterceptorChain进行process后获得Response,注意RealInterceptorChain中第四个参数是0,这个很有用。
现在进入RealInterceptorChain看看process方法:
前面一段是interceptor是合法性检测,不是我们的重点,重点是这个:
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors,
streamAllocation,
httpCodec,
connection,
index + 1,
request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
最后直接interceptor.intercept(next)返回了Response,那好,我们来看看这个Interceptor接口是什么吧:
可以看到,Interceptor.intercept中传入的是一个Chain接口,重点还是看段代码,我又贴了一遍:
RealInterceptorChain next = new RealInterceptorChain(interceptors,
streamAllocation,
httpCodec,
connection,
**index + 1**,
request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
光说图不是很明白,来张灵魂画工,估计能明白一点吧:
好了,看这张图我想你能明白我在说什么了,就是通过Chain的方式,将我们的Interceptor串联起来执行的,而且这个Chain名词也很形象,本身含义是链条的意思,的确啊,将所有的Interceptor连接起来,不就是链条么?这种模式我们称之为责任链模式,可能架构多了,很多东西都一样了吧,在web三个框架中Struct中,拦截器也是这么写的,使用责任链模式,非常的优雅和有效,值得我们学习啊。
至于到最后一个Interceptor.Intercept总该返回我们需要的Response吧,来看看最后一个Interceptor是什么了吧,不要忘记这张图啊:
最后一个是什么呢?是CallServerInterceptor,不说了,先进去看看intercept方法吧:
@Override
public Response intercept(Chain chain) throws IOException {
...
Response response = responseBuilder
.request(request)
.handshake(streamAllocation.connection().handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
int code = response.code();
if (forWebSocket && code == 101) {
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
....
return response;
}
代码都省略了啊,可以看出,最后一个使我们真正请求的Interceptor,并在这里完成了Response的组装,最后进行了返回,这个类里面涉及了很多http的知识,三次握手,返回码的知识等等,有时间再学习学习吧,这次写的还是有点多了。
通过简单的学习,我们了解一次OKHttp的请求,还是蛮复杂的,不能不说这样的设计真的需要水平,膜拜一下啊,当然了,很多地方也可能有错误或者分析不到位,希望大家指出啊,我也是只是写下自己的想法,大家共同进步吧。