探究OkHttpClient的运行原理(1)
网络连接是 app 最重要的功能,而 Okhttp 的使用在 android 开发中十分广泛。本篇文章将对整个OkHttp进行相关源码解析,以此能够对 Okhttp 框架有个整体的认识。
前言
OkHttp 是 Suqare 推出的在 Android 中使用的网络连接库。可以在 github 中搜索 okhttp 第一个选项即为相关内容:
https://github.com/square/okhttp/pulse
git 中对 okhhtp 的简介我们可以看到:
1583571724(1).png
HTTP 是现代应用常用的一种交换数据和媒体的网络方式,高效地使用 HTTP 能让资源加载更快,节省带宽。OkHttp 是一个高效的 HTTP 客户端,它有以下默认特性:
支持 HTTP/2,允许所有同一个主机地址的请求共享同一个 socket 连接
连接池减少请求延时
透明的 GZIP 压缩减少响应数据的大小
缓存响应内容,避免一些完全重复的请求
当网络出现问题的时候 OkHttp 依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个 IP 地址,当第一个IP请求失败时, OkHttp 会交替尝试你配置的其他 IP, OkHttp 使用现代TLS技术 (SNI, ALPN) 初始化新的连接,当握手失败时会回退到TLS 1.0。
目前最新的 okhhtp 已经更新到4.X版本,而4.x版本则用了 kotlin 语言,为(因)了(为)方(不)便(会)我这边用了 3.10.0 的 okhttp 版本进行源码查看。
如何去使用okhttp呢
这里先列举一个异步请求简单的例子:
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("http://baidu.com")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure( Call call, IOException e) {
}
@Override
public void onResponse( Call call, Response response) throws IOException {
}
});
按照上面的请求代码,我们一步步进行解析。
1 构造函数OkHttpClient okHttpClient = new OkHttpClient(),点进构造方法去看看:
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
可以看到上述代码 Okhttpclinet 的构造方法初始化了一些参数,以及一些 builder 的变量信息。
2 继续顺着代码往下看,Request request = new Request.Builder(),Request 的构造方法:
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
这里可以看到 Request 的构造办法也是主要去初始化了 http 请求的一些属性参数。包括url,方法,请求头,请求体等。
3 继续往下生成 Call 的方法,Call call = okHttpClient.newCall(request):
OkHttpClinet中:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
调用了Recall的newRealCall方法:
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
Okhttpclient 通过调用 newCall 方法,而生成了 newCall 类进行返回,返回的 RealCall 类是 Call 的实现类。
4 call 执行异步请求方法,call.enqueue(new Callback() {},我们来看Call的具体实现类RealCall中的:
RealCall:
@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));
}
这里主要做了以下操作:
1 判断是否执行了异步请求,如果已经执行过则抛出异常
2 回调 callStart 方法
3 clien t的 diapatcher 调用 enequeue 方法,分发请求。
上面的 diapatcher 好像在哪里看到过了,我们回到 OkhttpClient 构造方法中,发现也有个 diapathcher 的初始化方法。这里就是这个 diapatcher 复制可以到 Okhttpclinet 初始化中查看。
那么接下来,我们就去看 Diapathcer 这个类和 enqueue 方法:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
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<>();
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;
}
结合上述几段代码进行分析,可以得出一下几点重要信息:
1 Dipatcher 维护了几个队列 ,readyAsyncCalls --准备中的队列 , runningAsyncCalls --正在运行的异步队列,runningSyncCalls --运行的同步队列(用于同步请求)
2 当正在运行的队列小于 maxRequests(可以看到是64)并且对此 Host 的请求小于 maxRequestsPerHost (可以看到是5)的时候,则将当前 call 请求放入正在运行的队列之中,否则放入到准备队列之中。
3 线程池去执行请求。
现在我们看到队列里面加入了请求,并去线程池会去执行请求。既然有加入运行队列,那么是在什么时候把
readyAsyncCalls 队列中转移?或者 runningAsyncCalls 中删除呢?
接下来我们就要去看看 Call 类中的 Runable 方法
runningAsyncCalls 集合类是 AsyncCall 对象类的集合,进入到AsyncCall 类,它是 RealCall 的内部类:
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);
}
}
}
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
可以看到 AsyncCall 本质就是一个 Runnable 的实现类,实际下它最终会执行 AsyncCall 类中的 execute 方法,在execute 方法中我们可以发现:
1 根据执行流程序列会依次调用相关的回调。
2 Response response = getResponseWithInterceptorChain();这个方法是主要的请求获取到 Response 方法,这里先放着,后面会对此方法进行详细分析。
3 client.dispatcher().finished(this);对异步的队列进行操作。
进入到 Dispatcher 类中查看 finish 方法:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
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();
}
}
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();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
按照代码执行顺序依次查看可以看到 finish 方法中 promoteCalls 方法中会把当前 Call 从 runningAsyncCalls 中集合中移除,同时readyAsyncCalls 的数据转移到 runningAsyncCalls 中去执行。
至此,我们对Okhttp异步请求的数据以及队列的操作已经有了初步的认识。
其中主要涉及到 OkhttpClinet,Request,Call,Diapatcher相关类起到重要作用。
火星瓢虫_001
本篇文章先分析到这里,下一篇文章我们继续详细分析getResponseWithInterceptorChain() 方法。