核心类:OkHttpClient Call Request Response Dispatcher Interceptor
Request、Response比较好理解,请求数据和返回数据
OkHttpClient 使用new OkHttpClient()或new OkHttpClient.Builder().build()创建OkHttpClient实例,来进行网络请求。默认会创建ConnectionPool、Dispatcher、SocketFactory等。
Dispatcher 一个OkHttpClient的所有请求队列、分发
Call 接口,一次具体请求的封装,会再调用Dispatcher进行处理。
Transmitter Exchange
网络请求流程
先看下流程图
一、execute()同步请求 创建RealCall对象 流程:(没有创建新的线程)- 调用newCall创建Call对象,具体创建RealCall对象,并创建Transmitter对象。
- 调用Call对象的execute()方法。
- 调用Dispatcher的execute()方法,将RealCall添加到Dispatcher中的runningSyncCalls队列
- 处理所有拦截器,包括用户添加的,还有OkHttp自己内部创建的。
- gerResponseWithInterceptorChain()网络请求和返回,由拦截器封装和处理。
(1) RetryAndFollowUpInterceptor 重试和重定向拦截器
调用Transmitter.prepareToConnect()方法,创建ExchangeFinder对象。
(2) BridgeInterceptor 桥接拦截器,负责请求构建和响应结果封装。
(3) CacheInterceptor 缓存拦截器,负责缓存网络数据
(4) ConnectInterceptor 连接拦截器,负责socket的IO操作,使用的是Okio封装的类。(使用Exchange类)
调用ExchangeFinder.find()创建RealConnection对象,并创建Socket连接。返回创建Exchange和ExchangeCodec对象。
ExchangeCodec是接口,具体实现是Http1ExchangeCodec和Http2ExchangeCodec.
(5) CallServerInterceptor 访问服务拦截器,最后一个拦截器。向服务器发送Request请求,即将请求header和body写入socket,然后读取响应的header和body,创建返回需要的Response对象。(使用Exchange类)
通过Exchange发送Request和获取Response。
复制代码
-
调用Dispatcher中的finished()方法
(1) 从runningSyncCalls队列中移除。 (2) 调用promoteAndExecute()方法,处理readyAsyncCalls队列中的网络请求。 复制代码
二、enqueue()异步请求 创建AsyncCall对象,Callback异步请求的回调。
流程:
- 调用newCall创建Call对象,具体创建RealCall对象,并创建Transmitter对象。
- 调用Call对象的enqueue方法,创建AsyncCall对象,AsyncCall继承NamedRunnable,实现Runnable,即是个线程类。
- 调用Dispatcher的enqueue()方法,将AsyncCall添加到Dispatcher中的readyAsyncCalls队列。
- 调用promoteAndExecute()方法,从readyAsyncCalls获取AsyncCall对象,利用线程池,执行当前AsyncCall线程。
- NamedRunnable的线程run方法,会再调用子类的execute()方法。即AsyncCall的execute()方法。
- AsyncCall的execute()方法会调用gerResponseWithInterceptorChain(),处理拦截器。获取返回Response对象
- 将返回的Response对象,返回给Callback。
- 最后调用Dispatcher中的finished()方法
OkHttp扩展知识
- 请求任务缓存使用了Dequeue Queue Dequeue ArrayQueue
Queue 也是 Java 集合框架中定义的一种接口,直接继承自 Collection 接口。Queue 接口还定义一组针对队列的特殊操作。
Deque 接口继承自 Queue接口,但 Deque 支持同时从两端添加或移除元素,因此又被成为双端队列。鉴于此,Deque 接口的实现可以被当作 FIFO队列使用,也可以当作LIFO队列(栈)来使用。官方也是推荐使用 Deque 的实现来替代 Stack。
ArrayDeque 是 Deque接口的一种具体实现,维护一个数组和首尾位置。如果数组容量已满,则会创建新的2倍容量的数组,并赋值。
/**
* The array in which the elements of the deque are stored.
* The capacity of the deque is the length of this array, which is
* always a power of two. The array is never allowed to become
* full, except transiently within an addX method where it is
* resized (see doubleCapacity) immediately upon becoming full,
* thus avoiding head and tail wrapping around to equal each
* other. We also guarantee that all array cells not holding
* deque elements are always null.
*/
transient Object[] elements; // non-private to simplify nested class access
/**
* The index of the element at the head of the deque (which is the
* element that would be removed by remove() or pop()); or an
* arbitrary number equal to tail if the deque is empty.
*/
transient int head;//数组首元素位置
/**
* The index at which the next element would be added to the tail
* of the deque (via addLast(E), add(E), or push(E)).
*/
transient int tail;//数组尾元素的下个位置
复制代码
在 ArrayDeque 中数组是当作环形来使用的,在容量保证为 2^n 的情况下,仅仅通过位与操作就可以完成环形索引的计算,而不需要进行边界的判断,在实现上更为高效。
环形结构:
位与运算:新位置和(elements.length - 1)做与运算
先看添加头:
head的新位置一定是head-1,如果得到的结果为-1,就要挪到右侧,也就是物理数组的最后一个位置length-1。
if (head-1 >= 0) {
head=head-1;
} else {
head=elements.length -1;
}
elements[head]=e;
复制代码
而ArrayDeque使用了性能更好的位与运算
elements[head = (head - 1) & (elements.length - 1)] = e;
复制代码
相比ArrayList,我们可以看到ArrayDeque大量减少了System.arrayCopy的使用。
- 线程池
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;
}
复制代码
SynchronousQueue 阻塞队列 BlockingQueue 同步锁 ReentrantLock