okhttp
是Square
公司开源的一个非常便捷的轻量级第三方网络访问框架。它支持同步请求和异步请求。使用起来也是非常的方便。用的人也是越来越多。作为有追求的码农,我们当然要知其怎么用,也要知道它是怎么实现的。那就闲话少说,马上发车。下面就是我自己总结的OkHttp流程图:
整个
OkHttp
的请求大致就是上面图片里面的过程,下面我将按照上面的流程图来一步一步解析okhttp
的请求过程;
1、OkHttpClient
的创建
OkHttpClient
的创建我们一般都会选择使用OkHttpClient.Builder
,这里我们可以设置一些我们发起请求的一些设置,比如我们自定义的拦截器,缓存啊,超时控制等,可以设置的参数,这里就不具体说了,我们在实际应用的时候自己按需要设置吧。
mOkHttpClient = new OkHttpClient.Builder()
.callTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
2、调用newCall(Request request)
创建RealCall
RealCall
是我们真正发起请求的类。它拥有两个比较重要的成员变量client
和originalRequest
。client
当然好理解了,就是OkHttpClient
的实例了,为什么持有他也很好理解了,既然是实际发起请求的类,客户端手动设置的请求设置当然也要起作用了。originalRequest
是Request
的实例,我们都知道Http请求包括请求头,请求链接,请求体等等,没错这个类就是包含这些东西的类。Request
拥有ulr
、method
、headers
和RequestBody
这四个重要的成员变量,ulr
代表请求链接,method
代表请求方式(get,post等等),headers
就是请求头了,RequestBody
对应就是请求体了,像post请求就会设置请求体。下面代码表示的就是一个发起post的请求的Request了
private RequestBody createPostRequestBody(Map<String, Object> params) {
FormBody.Builder bodyBuilder = new FormBody.Builder();
if (params == null || params.isEmpty())
return bodyBuilder.build();
for (Map.Entry<String, Object> entry : params.entrySet()) {
bodyBuilder.add(entry.getKey(), entry.getValue().toString());
}
return bodyBuilder.build();
}
Request request = new Request.Builder()
.post(createPostRequestBody(params))
.url(url)
.build();
3、发起请求
请求方式有两种,一种是同步请求,一种是异步请求。我们就先讲讲异步请求了。
RealCall
的异步请求依赖Dispatcher
类来执行。这个类是用来管理异步请求的,它有如下的成员:
//异步请求最大并发请求数为64
private int maxRequests = 64;
//同一主机的异步请求最大并发请求数
private int maxRequestsPerHost = 5;
Runnable idleCallback;
//请求执行的线程池
private ExecutorService executorService;
//将要执行的异步请求
private final Deque<RealCall.AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在执行的异步请求
private final Deque<RealCall.AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在执行的同步请求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
它的执行逻辑大概是,先将异步请求放到readyAsyncCalls 等待执行,然后遍历readyAsyncCalls ,如果正在执行的请求数没有达到最大请求数,并且该请求请求的主机也没有到达最大请求书,就会发请求。
通过Dispatcher
最终异步请求的执行会转移到AsyncCall
的execute方法,接下来就是第四部了。同步请求就比较简单,各位看官们自己去看看源码吧!
4、执行getResponseWithInterceptorChain()
朋友们,终于来到了最核心的地方来了!这个方法里面使用责任链模式来执行拦截器,有点像View的事件传递。拦截器的执行顺序是先执行我们自定义的拦截器,然后是RetryAndFollowUpInterceptor
、BridgeInterceptor
、CacheInterceptor
、ConnectInterceptor
、networkInterceptors
、CallServerInterceptor
。这些拦截器是通过RealInterceptorChain
一个一个连接起来,按顺序执行。RealInterceptorChain
的最核心方法是proceed,这个方法的返回值是Response,这个东西我们就很熟悉嘞。proceed执行大致是:先创建一个新的RealInterceptorChain 对象next,这个next的拦截器下标加1,然后根据下标取出当前拦截器执行interdept方法,interdept方法又会执行next的proceed.。最终执行的轨迹是请求Request被一步步向下包装直到发起请求得到Response,然后Response被一步步向上处理,直到最顶层返回。接下来就是一步步分析拦截器的功能了。
// 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);
5、RetryAndFollowUpInterceptor
这个Interceptor
是处理失败重试和重定向处理。大致是经过下面几个检查
如果连接失败(未发起请求连接失败,请求已发送但连接中断),检查请求是否可以重试,如果可以就发起重试,否则任务请求失败;
请求成功,但需要判断请求是否需要重定向;判断是否重定向主要依据responseCode来判断以及发起请求的请求体是否支持多次请求。
6、BridgeInterceptor
顾名思义这是一个连接应用层代码和网络代码的桥梁,主要的功能是把我们写的请求代码翻译成http请求能识别的代码,然后把网络返回的Response翻译成我们可以识别的代码。
7、NetworkInterceptors
配置OkHttpClient时设置的 NetworkInterceptors。
8、CacheInterceptor
从名字上我们就知道这和缓存有关。
- 首先根据请求读取缓存
- 判断缓存是否可用
- 使用如果没有缓存,使用请求;
- 如果当前请求是https,但是缓存在请求的时候是否经过TLS 握手,如果没有,使用请求(这一块对网络请求不太了解,请大佬补充);
- 缓存是否应该被缓存,以及当前请求结果是否应该被缓存,如果不应该,使用网络请求;
- 如果请求设置了不使用缓存,或者请求了设置了If-Modified-Since或If-None-Match,使用网络请求;
- 检查http请求的缓存使用条件(这一块不是很懂,就不贻笑大方了);
- 如果经过上面的判断后,还是需要发起网络请求,并且当前请求设置不使用网络请求,这时候就认为请求失败了,cede是504;
- 网络请求成功,再判断是使用网络请求结果还是缓存请求结果,以及更新缓存;
8、ConnectInterceptor
这里主要是建立和服务器的连接通道。这里面涉及到通道的创建和复用,这里及不详细说了(其实我也不太懂,有时间再研究)。
9、CallServerInterceptor
向服务器发起请求。主要是向通道写入请求头,如果有请求体,也写入请求体,然后通过这个通道读取服务器返回的响应。