想必一提到 Android 网络库 就会想到 OkHttp,今天就来分析一下 OkHttp 里面的整个整个流程
前言
注:本文 OkHttp 源码解析基于 v3.8.1 ,下文中涉及到展示 OkHttp 源码的地方,都采用在 AS 里打开源码并以截图的方式展现出来,这样更加直观。
一、简单使用
请求分为同步和异步两种:
- 同步请求通过 调用 Call.exectute() 方法直接返回当前请求的 Response;
- 异步请求调用 Call.enqueue() 方法将请求(AsyncCall)添加到请求队列中去,并通过回调(Callback)获取服务器返回的结果。
代码如下:
//1.新建OKHttpClient客户端
OkHttpClient mClient = new OkHttpClient();
//2.新建一个Request对象
Request mRequest = new Request.Builder()
.url(URL)
.build();
//3.Response为OKHttp中的响应
//(1)同步请求
Response response = mClient.newCall(mRequest).execute();
//(2)异步请求
Response response = mClient.newCall(mRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
二、流程分析
从上面可以看到无论是 同步请求 还是 异步 请求,前两个步骤都是一致的,接下来一步一步来分析流程。
第一步,先是 实例化 了一个 OKHttpClient 对象,这个 OKHttpClient 类就比较简单了,里面包含了很多对象,其实OKhttp的很多功能模块都包装进这个类,让这个类单独提供对外的 API,这种 外观模式的设计十分的优雅。而内部模块比较多,就使用了Builder 模式(建造者模式)。我们看下 OKHttpClient 的构造方法,代码如下:
第二步,构建一个 Request 对象,Request 和 第三步中的 Response 分别抽象成 请求 和 响应。
其中 Request 包括 Headers 和 RequestBody,而 RequestBody 是 abstract 的,他的子类有 FormBody (表单提交的)和 MultipartBody(文件上传),分别对应了两种不同的 MIME 类型,FormBody :"application/x-www-form-urlencoded",MultipartBody:"multipart/"+xxx。Response 包括 Headers 和 ResponseBody,而 ResponseBody 是 abstract 的,所以他的子类也是有两个,RealResponseBody 和 CacheResponseBody,分别代表真实响应和缓存响应。
第三步,同步和异步都是先传入第二步中的 Request 对象来调用第一步中的 OkHttpClient 对象的 newCall(Request request)方法。我们看看这个 newCall() 方法里面做了什么,代码如下:
前面第三步说到有两种请求方式:
- 同步请求
-
异步请求
刚才说过一切方法都是通过 Call 这个接口里的方法来实现的,而 RealCall 又是 Call 的唯一实现类,因此同步和异步方法分别执行的是RealCall 里的方法:
同步 & 异步Dispatcher分别指的是:
- 最大连接数:64
- 同一端口允许最大连接数:5
- 等待队列
- 正在执行异步队列
- 正在执行同步队列
带着这几个变量来看 enqueue 方法:Dispatcher#enqueuepromoteAndExecute
整个方法的作用就是:从等待队列中取出一个请求,判断此时正在执行的队列数是否超过64,以及同一端口的请求数是否超过5,如果都不超过的话就将这个请求加入到执行队列中。
而在第202-205行则是循环从执行队列中取出请求,在204行通过AsyncCall 执行 executeOn 方法执行请求。注意这里传入了 executorService() :
- 这里的核心线程数为0
- 同时这里传入的队列 SynchronousQueue 有个特点,容量为0
这两个特点结合 线程池的特点 可以得到一个结论就是新加入的请求不会被堵塞住而会马上执行。
接下来看到 AnsyncCall 方法:AnsyncCallNameRunable
AsyncCall 是 RealCall 的内部类,继承于 NamedRunnable 类,而 NamedRunnable 又实现了 Runnable 接口,所以 AsyncCall 本质上是一个 Runable。在 NameRunnable 中执行了 run方法,而 run方法 执行了 execute 方法,也就是执行了 AsyncCall 中的 execute 方法:AsyncCall # execute
finish
最后回过头来说一下刚才的 getResponseWithInterceptorChain() 方法,也就是真正获取响应的方法:getResponseWithInterceptorChain
- client.interceptors():用户自定义的 Interceptor。(第205行设置)
- RetryAndFollowUpInterceptor:负责失败重试以及重定向。
- BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。
- CacheInterceptor:负责读取缓存以及更新缓存。
- ConnectInterceptor:负责与服务器建立连接。
- client.newworkInterceptors():用户自定义的网络层 Interceptor(在图 3-1 的 211 行设置)
- CallServerInterceptor:负责从服务器读取响应的数据。
位置决定功能,位置靠前的先执行(按以上顺序),最后一个则负责与服务器通讯,请求从 RetryAndFollowUpInterceptor (如果没有用户自定义的 Interceptor)开始层层传递到 CallServerInterceptor,每一层都对请求做相应的处理,处理的结果再从 CallServerInterceptor 层层返回给 RetryAndFollowUpInterceptor,最后请求的发起者获得了服务器返回的结果。注意这里第215行 的传入的index 值为 0 先来看下 Interceptor 这个接口的代码:
每个拦截器的方法都遵循这样的规则:
@Override public Response intercept(Chain chain) throws IOException {
//1 Request阶段,该拦截器在Request阶段负责做的事情
Request request = chain.request();
//2 调用RealInterceptorChain.proceed(),其实是在递归调用下一个拦截器的intercept()方法
response = ((RealInterceptorChain) chain).proceed(request, transmitter, null);
//3 Response阶段,完成了该拦截器在Response阶段负责做的事情,然后返回到上一层的拦截器。
return response;
}
从上面的描述可知,Request 是按照 interpretors 的顺序正向处理,而 Response 是逆向处理的。它的实现采用责任链模式,这参考了OSI七层模型的原理。上面我们也提到过。CallServerInterceptor 相当于最底层的物理层, 请求从上到逐层包装下发,响应从下到上再逐层包装返回。
流程如下: