从零开始的 OkHttp

OkHttp源码流程

答案参考自:

基本流程

  1. 创建 OkHttpClient 对象
  2. 创建 Request 对象
  3. 创建 Call 对象
  4. 同步请求调用 call.execute();异步请求调用 call.enqueue()。

OkHttp线程池

答案参考自:

OkHttp 的异步请求是基于其内部的默认的线程池实现的。

而默认的线程池是在 Dispatcher 被实例化的。

1
2
3
4
5
6
7
8
9
10
11
12
13
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(
0,
Integer.MAX_VALUE,
60,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Util.threadFactory("OkHttp Dispatcher", false)
);
}
return executorService;
}

该线程池的核心线程数为 0,线程池最有能容纳 Integer.MAX_VALUE 个线程,且线程的空闲存活时间为 60s(可以理解为 OkHttp 随时可以创建新的线程来满足需要。可以保证网络的 I/O 任务有线程来处理,不被阻塞)。

重点是使用的阻塞队列为 SynchronousQueue,参考:
[java1.8源码笔记]SynchronousQueue详解 | LuoMing’s Blog (luoming1224.github.io)

OkHttp拦截器

getResponseWithInterceptorChain()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
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));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}

可以看到,首先,他会将客户端的 interceptors 添加到 List 当中,接着,再添加 okhttp 里面的 interceptor,然后构建了一个 RealInterceptorChain 对象,并将我们的 List<Interceptor> 作为成员变量,最后调用 RealInterceptorChainproceed 方法。

其中,client.interceptors() 为我们自己添加的请求拦截器,通常是做一些添加统一的 token 之类操作。

其余较重要的拦截器

RetryAndFollowUpInterceptor 拦截器

  • RetryAndFollowUpInterceptor 此拦截器顾名思义就是主要负责失败重连工作,但是并不是所有的网络请求都会进行失败重连的,在此拦截器内部会进行网络请求的异常检测和响应码的判断,如果都在限制范围内,那么就可以进行失败重连。
  • 一个循环来不停的获取response。每循环一次都会获取下一个request,如果没有,则返回response,退出循环。而获取下一个request的逻辑,是根据上一个response返回的状态码,分别作处理。

桥接拦截器

  1. 请求从应用层数据类型类型转化为网络调用层的数据类型。
  2. 将网络层返回的数据类型 转化为 应用层数据类型。

CacheInterceptor 拦截器

  • 作用:将HTPTP和HTTPS的网络返回数据缓存到文件系统中,以便在服务端数据没发生变化的情况下复用,节省时间和带宽;

连接拦截器 和 最后的请求服务器的拦截器

这两个连接器基本上完成了最后发起网络请求的工作。

addInterceptoraddNetworkInterceptor区别

答案参考自:

从上文构造拦截器链的过程,可以看到,通过 addInterceptor() 方法添加的拦截器是放在最前面的。

而通过 addNetworkInterceptor() 方法添加的网络拦截器,则是在非 WebSocket 请求时,添加在 ConnectInterceptorCallServerInterceptor 之间的。

addInterceptor(应用拦截器)

  1. 不需要担心中间过程的响应,如重定向和重试.
  2. 总是只调用一次,即使 HTTP 响应是从缓存中获取.
  3. 观察应用程序的初衷. 不关心 OkHttp 注入的头信息如: If-None-Match.
  4. 允许短路而不调用 Chain#proceed,即中止调用.
  5. 允许重试,使 Chain#proceed 调用多次.

addNetworkInterceptor(网络拦截器)

  1. 能够操作中间过程的响应,如重定向和重试.
  2. 当网络短路而返回缓存响应时不被调用.
  3. 只观察在网络上传输的数据.
  4. 携带请求来访问连接.

OkHttp责任链模式

proceed()

proceed v.继续做(或从事、进行);接着做;继而做;行进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) throws IOException {

// 省略无关代码

// 生成 list 当中下一个 interceptot 的 chain 对象
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
// 当前的 interceptor
Interceptor interceptor = interceptors.get(index);
// 当前的 intercept 处理下一个 intercept 包装的 chain 对象
Response response = interceptor.intercept(next);

// ...

return response;
}

proceed 方法也很简单,proceed方法每次从拦截器列表中取出拦截器,并调用 interceptor.intercept(next)。

熟悉 Okhttp 的应该都知道,我们在 addInterceptor 创建 Interceptor 实例,最终都会调用 chain.proceed(Request request),从而形成一种链式调用。这种便是责任链设计模式。

1
2
3
4
5
6
7
8
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder builder = request.newBuilder().addHeader("name","test");
return chain.proceed(builder.build());
}
}).build();

而 OkHttp 是怎样结束循环调用的,这是因为最后一个拦截器 CallServerInterceptor 并没有调用 chain.proceed(request),所以能够结束循环调用。

OkHttp缓存怎么处理

答案参考自:

更多推荐文章


从零开始的 OkHttp
https://luoyuy.top/posts/7744c4ecc6a9/
作者
LuoYu-Ying
发布于
2023年3月3日
许可协议