okhttp解析

okhttp 是一个网络框架,是当下android使用最频繁的网络请求框架,由Square公司开源。

google在Android4.4后开始将源码中的HttpUrlConnection底层实现替换为okhttp,现在流程的Retrofit框架底层同样是使用okhttp的。

优点

  • 支持http1、http2、Quic以及WebSocket
  • 连接池复用底层TCP(Socket),减少请求延时
  • 无缝支持GZIP减少通信数据流量
  • 缓存相应数据减少重复请求次数
  • 请求失败自动重试主机其他ip,自动重定向

使用方法

okhttp具体使用可以查看:

Call

在client执行request时,会调用newCall方法。

1
2
3
4
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}

会返回一个RealCall对象,而RealCall是Call接口的一个实现。

client调用newCall时,只是返回一个RealCall对象,请求还没有发送出去。
需要执行RealCall的execute()或者enqueue()

这两个方法由什么区别呢,现在看一下。

execute

这是一个同步方法,即会直接执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
// 发起请求
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}

此处会调用client.dispatcher().executed(this); 其实是将当前RealCall对象加入到Dispatch的同步执行队列
在最后执行完毕会调用 client.dispatcher().finished(this); 其实是将当前RealCall对象从Dispatch的同步执行队列移除,因为当前RealCall对象的任务已执行完毕

会获取到response,并直接return

enqueue

这是一个异步方法

1
2
3
4
5
6
7
8
9
10
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

在上面的两个方法中,最后都会调用到dispatcher的对应函数,Dispatch是一个分发器

注意调用dispatch的enqueue函数时,会将responseCallback包装成一个AsyncCall对象,
AsyncCall为RealCall的一个内部类

AsyncCall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;

AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}

String host() {
return originalRequest.url().host();
}

Request request() {
return originalRequest;
}

RealCall get() {
return RealCall.this;
}

@Override
protected void execute() {
boolean signalledCallback = false;
try {
//执行请求 (拦截器)
Response response = getResponseWithInterceptorChain();

if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}

AysncCall实质上实现的是Runnable,execute执行的是获取response过程,
请求成功则执行responseCallback.onResponse,否则执行responseCallback.onFailure

最后执行client.dispatcher().finished(this); 注意最后传入的this指向是AsyncCall实例对象,

Dispatch

Dispatch是okHttp内一个任务分发器,用于管理任务、管理线程池。
我们可以自定义Dispatch,如果没有传入自定义的,okHttp内部也有默认的,okhttp3.Dispatcher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable
Runnable idleCallback;

/**
* Executes calls. Created lazily.
* 内部的线程池
*/
private @Nullable
ExecutorService executorService;

/**
* Ready async calls in the order they'll be run.
* 等待执行的异步队列
*/
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

/**
* Running asynchronous calls. Includes canceled calls that haven't finished yet.
* 正在执行的异步队列
*/
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

/**
* Running synchronous calls. Includes canceled calls that haven't finished yet.
* 正在执行的同步队列
*/
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

....
}

可以看到在Dispatch内部有一个线程池,有三个队列,这三个队列下面会用的,会根据不同的任务添加到不同的队列

看一下Dispatch内部的executed以及enqueue函数

线程池

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

关于线程池的详细解析可以查看之前的文章:
线程池第4个参数,是传入一个等待队列,一般来说等待队列有三种:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue

  • ArrayBlocking: 基于数组的等待队列,初始化需要指定固定大小
    当使用此队列,向线程池提交任务时,会首先加入到等待队列中,当等待队列满了,提交到队列中失败时,就是就会检查线程池的最大线程数是否达到最大,没有则启动线程执行任务。
    所以最终可能出现后提交的任务先执行,而先提交的任务还在等待队列等待执行。
  • LinkedBlockingQueue: 基于链表的等待队列,初始化可以指定大小,也可以不指定
    当指定大小后,表现就和 ArrayBlockingQueue 一致,如果没指定大小,则会使用默认的integer.MAX_VALUE,此时添加任务会一直成功。
    最终所有的任务都会在核心线程中执行,如果核心线程线程被占用,则会一直等待。
  • SynchronousQueue: 无容量的队列
    使用此队列就是希望拥有最大的并发量,因为无论如何,向队列中添加任务都会失败,此时就会一直检查线程池线程数是否达到最大线程数,没有达到则会一直新建线程来执行任务。
    唯一制约就是配置的最大线程数,如果使用Integer.MAX_VALUE就实现了真正的无等待。

而在okhttp中就采用的SynchronousQueue,但进程内存是有限制的,不能无限添加,所以在Okhttp内部又有maxRequests限制,保证不超过64个。

executed

1
2
3
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}

可以看到在同步函数中,会将call 添加到同步执行队列

enqueue

1
2
3
4
5
6
7
8
9
10
synchronized void enqueue(AsyncCall call) {
//todo : 1、如果正在执行的请求小于64
// 2、相同host的请求不能超过5个
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call); //正在执行的请求
executorService().execute(call); //线程池跑任务
} else {
readyAsyncCalls.add(call);
}
}

在异步执行时,会判断当前正在执行异步队列大大小是否小于64 && 传入的call的Host在正在执行队列中数量是否小于,
如果都满足,则会被放入正在执行异步队列,并且调用线程池执行该任务
如果不满足,则会被放入待执行异步队列

注意:此处的call为一个AsyncCall实例,

上面在AsyncCall中讲过了,当AsyncCall执行完毕会调用finished(AsyncCall)函数来移除正在执行异步队列中的这个AsyncCall对象

finished

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 异步执行队列移除AsyncCall对象
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}

// 同步队列移除RealCall对象
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}

private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}

if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}

当异步执行队列移除AsyncCall对象时,会执行promoteCalls();
而且当this.idleCallback不为空,而且异步执行队列为空时,
会执行this.idleCallback.run();

promoteCalls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 从异步等待队列中取任务执行
*/
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
// 同一Host请求只能同时有5个
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}

if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}

promoteCalls函数主题其实就是从等待异步队列中取任务放入到执行异步任务队列中,并执行。

  • 先判断异步执行队列大小是否大于64,是则不向下执行
  • 再判断待执行异步队列是否没有数据,是则不向下执行
  • 然后遍历readyAsyncCalls队列,判断当前AsyncCall的Host是否在异步执行队列中<5,
    小于5时,将该任务从待执行异步队列移除,并将该任务添加到执行异步队列,并放入线程池
    判断异步执行队列大小是否大于64,是的话,终止遍历

到这里,okHttp的任务执行基本就看完了。

当然还有任务的取消

Cancel

1
2
3
4
5
6
7
8
9
10
11
12
13
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.get().cancel();
}

for (AsyncCall call : runningAsyncCalls) {
call.get().cancel();
}

for (RealCall call : runningSyncCalls) {
call.cancel();
}
}

就是遍历三个队列,调用Call对象的cancel方法。