OkHttp 是一款用于 Android 和 Java 的网络请求库,也是目前 Android 中最火的一个网络库。OkHttp 有很多的优点:
- 在 HTTP/2 上允许对同一个 host 的请求共同一个 socket
- 连接池的使用减少请求延迟(如果 HTTP/2 不支持)
- 透明的 GZIP 压缩减少数据量大小
- 响应的缓存避免重复的网络请求
1.基本用法
// 构建okHttpClient,相当于请求的客户端
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
// 构建一个请求体
Request request = new Request.Builder()
.url(url)
.build();
// 生成一个Call对象,该对象是接口类型
Call call = okHttpClient.newCall(request);
// 同步
Response response = call.execute();
// 异步
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
- 通过Builder模式创建OkHttpClient对象和Request对象
- 调用OkHttpClient的newCall方法,获取一个Call对象,参数是Request
- 调用execute/enqueue方法获取一个Respone
2.源码分析
1.OkHttpClient、Request 及 Call 的创建
OkHttpClient 的创建采用了 Builder 模式,可以配置 Interceptor、Cache 等。可以设置的参数很多,其中部分参数如下:
public static final class Builder {
Dispatcher dispatcher;
...
...
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
Builder是OkHttpClient一个静态内部类,在Builder的构造函数中进行了一系列的初始化操作,其中Dispatcher中文是分发器的意思,和拦截器不同的是分发器不做事件处理,只做事件流向。他负责将每一次Requst进行分发,压栈到自己的线程池,并通过调用者自己不同的方式进行异步和同步处理,那具体是怎么操作的呢?后面会讲。ConnectionPool是一个连接池对象,它可以用来管理连接对象,从它的构造方法中可以看到连接池的默认空闲连接数为5个,keepAlive时间为5分钟。
Request 与 OkHttpClient 的创建类似,也是用了 Buidler 模式,但是其参数要少很多:
public static class Builder {
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
}
参数的含义都很明确,即使 Http 协议的url、header、method 以及 body 部分。变量 tag 用于标识一条 Request,可用于发送后取消这条请求。
client.newCall(request) 生成一个 Call 对象。Call 实际上是一个接口,它封装了 Request,并且用于发起实际的网络请求。下面是 Call 的全部代码:
public interface Call extends Cloneable {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
Call clone();
interface Factory {
Call newCall(Request request);
}
}
其中包含了与网络请求相关的操作,包括发起、取消等。看一下 OkHttpClient 是如何创建 Call 的:
# OKHttpClient.java
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
#RealCall.java
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;
}
从代码可以看到,实际上是创建了一个 RealCall 对象,它也是 Call 的唯一一个实现类。
有了 RealCall 对象后,就可以发起网络请求了,可以是同步请求(execute)或者是异步请求(enqueue)。异步请求涉及到 Dispatcher,先从相对简单的同步请求开始分析。
2.同步请求
调用 RealCall#execute() 即是发起同步请求,代码如下:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
首先判断这条请求是不是已经执行过,如果是则会抛出异常(一条请求只能执行一次,重复执行可以调用 Call#clone())。接着执行了 client.dispatcher().executed(this),这行代码是把当前的 Call 加入到 Dispatcher 的一个队列中,这个暂时可以忽略,后面会分析 Dispatcher。
下面一行 Response result = getResponseWithInterceptorChain()
是关键,在 getResponseWithInterceptorChain 中真正执行了网络请求并获得 Response 并返回。(下一小节具体分析其中的逻辑)
最后在 finally 中调用 Dispatcher 的 finished,从队列中移除这条请求。
3.拦截器Interceptor
getResponseWithInterceptorChain 的代码如下:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List 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);
}
可以看到,其中创建了一个 List 用于添加 Interceptor。首先添加的是 client 中的 interceptors,也就是在创建 OkHttpClient 对象时自定义的 interceptors,然后依次添加 retryAndFollowUpInterceptor(重试及重定向)、BridgeInterceptor(请求参数的添加)、CacheInterceptor(缓存)、ConnectInterceptor(开始连接)、用户自定义的 networkinterceptors 及 CallServerInterceptor(发送参数并读取响应)。从这里可以知道,OkHttp 默认添加了好几个 interceptor 用于完成不同的功能。
在研究各个 interceptor 之前,需要考虑一下如何让这些拦截器一个接着一个的执行?继续看上面的代码,在添加了各种 interceptors 之后,创建了一个 RealInterceptorChain 对象。(它的构造函数需要的参数很多,并且这些参数涉及到连接池、请求数据的发送等。由于这篇文章主要分析 OkHttp 的基本流程,所以暂时略过这部分)RealInterceptorChain 是接口 Chain 的实现类,Chain 是 链 的意思,其作用是把各个 Interceptor 串起来依次执行。在获得了 RealInterceptorChain 之后调用其 proceed 方法,看名字就能知道是让 Request 请求继续执行。
下面具体分析 RealInterceptorChain,它有如下的成员变量:
private final List interceptors; // 拦截器
private final StreamAllocation streamAllocation; // 流管理器
private final HttpCodec httpCodec; // http流,发送请求数据并读取响应数据
private final RealConnection connection; // scoket的连接
private final int index; // 当前拦截器的索引
private final Request request; // 当前的请求
private int calls; // chain 的 proceed 调用次数的记录
其中 streamAllocation、httpCodec 和 connection 都与 socket 连接有关,后续文章再分析。看一下 proceed 方法:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
// 如果已经有了一个流,确保即将到来的 request 是用它
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
// 如果已经有了一个流,确保这是对 call 唯一的调用
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request); // (1)
Interceptor interceptor = interceptors.get(index); // (2)
Response response = interceptor.intercept(next); // (3)
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
刚开始做了一些连接方面的判断,需要关注的是标了(1)、(2)、(3)的几行,主要做了以下操作:
1.创建新的 RealInterceptorChain,其中 index 加1用于标识当前的拦截器
2.通过 index 获取当前的拦截器
3.调用下一个拦截器的 intercept 方法,并把上面生成的新的 RealInterceptorChain 对象 next 传进去
由之前的getResponseWithInterceptorChain
方法可以知道,当前 RealInterceptorChain
的 interceptors 的第一个是 RetryAndFollowUpInterceptor
,下面是其 intercept 的代码:
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
// 调用 chain 的 proceed
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
... // 省略部分代码,主要是错误重试以及重定向
}
}
这个 Interceptor 主要用于出错重试以及重定向的逻辑,其中省略了部分代码。在这个方法当中要关注的是再次调用了 chain 的 proceed 方法,这里的 chain 是之前新创建的 next 对象。相当于说通过调用 Chain#proceed() 将网络请求推向下一个拦截器(proceed 中会获取下一个 Interceptor 并调用其 intercept 方法),并且得到 response 对象,而下一个拦截器也是类似的操作。于是,多个 interceptors 就通过这种方式串起来依次执行,并且前一个 Interceptor 可以得到后一个 Interceptor 执行后的 response 从而进行处理。
通过不同的 Interceptor,OkHttp 实现了不同的功能。各个 Inercept 职责分明又不会互相耦合,并且可以非常方便的添加 Interceptor,这是 责任链 模式的体现,非常优雅的设计。现在可以发现 OkHttp 中的拦截器的调用过程如下图所示:
4.异步请求
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));
}
相比于同步请求,异步请求主要是增加了 Dispatcher 的处理。Dispatcher 是请求的分发器,它有一下的成员变量:
private int maxRequests = 64; // 最大连接数
private int maxRequestsPerHost = 5; // 单个 host 最大连接数
private @Nullable Runnable idleCallback; // 空闲时的回调
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService; // 线程池
/** Ready async calls in the order they'll be run. */
// 准备执行的异步 Call 的队列
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
// 正在执行的的异步 Call 的队列
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
// 正在执行的同步 Call 的队列
private final Deque runningSyncCalls = new ArrayDeque<>();
在 Dispatcher 中,默认支持的最大并发连接数是64,每个 host 最多可以有5个并发请求。
下面看一下线程池 executorService 的创建。线程池会在两个地方创建,分别是 Dispatcher 的构造函数或者是 executorService 方法中(如果调用了默认的构造函数):
// 默认构造函数没有创建
public Dispatcher() {
}
// 自定义线程池
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
// 如果没有自定义线程池,则默认创建
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;
}
Dispatcher 支持自定义的线程池,否则会默认创建一个。在生成 OkHttpClient 对象时,默认调用的是 Dispatcher 无参的构造方法。这个默认线程池通过
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
创建,看上去类似于一个 CachedThreadPool,没有常驻的 core 线程,空闲线程60秒后自动关闭。
每个 Call 被添加到某一个队列,如果是同步请求添加到 runningSyncCalls 中:
#Dispatcher.java
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
异步请求添加的逻辑如下:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
具体步骤是:
1.判断是否超出总共的最大连接数以及单个 host 的最大连接数
2.如果没有则添加到 runningAsyncCalls 并且提交到线程池执行
3.否则添加到 readyAsyncCalls 等待后续执行
需要注意的是异步请求的 Call 不是原始的 Call,而是被包装为 AsyncCall
:
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
...
@Override protected void execute() {
boolean signalledCallback = false;
try {
// 调用 getResponseWithInterceptorChain
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
AsyncCall 继承自 NamedRunnable,它其实就是一个为线程设置了名字的 Runnable,在其 Run 中调用 execute,所以 AsyncCall 的主要逻辑都写在 execute 中。可以看到最终还是调用了 getResponseWithInterceptorChain 方法,所以后续执行网络请求的逻辑是一样的。在获得 response 之后,就可以调用 responseCallback 返回最终的信息。
Finish
在上面的代码中,finally 里面执行了 client.dispatcher().finished(this),在同步请求 RealCall#execute() 中也有类似的一行代码。finished 的作用是让 Dispatcher 从队列中移除已完成的 Call,对于异步请求还会从 readyAsyncCalls 中取出等待中的请求提交给线程池。下面是具体代码:
/** 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 void finished(Deque 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 i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
// 找到一个等待队列中的 Call,符合连接数要求时加入 runningAsyncCalls 并提交给线程池执行。
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
有两个重载的 finished 方法均调用了另一个 pirvate 的 finished,区别在于这个 finished 的最后一个参数 promoteCalls。对于同步请求(参数为 RealCall) promoteCalls 为 false,而异步请求(参数为 AsyncCall) promoteCalls 为 true。 pirvate 的 finished 主要是从队列中移除 Call,异步请求会执行 promoteCalls。promoteCalls 里面主要是从 readyAsyncCalls 取出一个 Call,如果满足最大连接数的要求,则把这个 Call 加入 runningAsyncCalls 并提交给线程池执行。
通过 runningAsyncCalls 和 readyAsyncCalls,Dispatcher 实现了异步请求的调度执行。这里比较巧妙的方式是在 finally 中去执行 readyAsyncCalls 中的请求,避免了 wait/notity 的方式,避免了代码的复杂性。
总结
OkHttp 的基本执行流程如下图所示:
主要是以下步骤:
1.OkHttpClient 调用 newCall 创建 RealCall 对象,Call 封装了 Request,代表一条即将执行的请求。
2.根据同步还是异步请求分别调用 RealCall 的 execute 或 enqueue 方法,将Call 加入 Dispatcher 的相应队列中。最终,同步或异步请求都会调用 getResponseWithInterceptorChain。
3.在 getResponseWithInterceptorChain 中,OkHttp 添加用户自定义以及默认的 inceptors,并用一个 Chain 管理并依次执行每个 Interceptor。
4.每个 Interceptor 调用 Chain#proceed() 将请求发送给下一级的 Inceptor,并能通过这个方法获得下一级 Interceptor 的 Response。所以上图所示,Request 一级级地往下传递,而获取了网络的 Response 之后一级级地往上传递。
延申问题:
OkHttp怎么实现连接池
为什么需要连接池?
频繁的进行建立Sokcet连接和断开Socket是非常消耗网络资源和浪费时间的,所以HTTP中的keepalive连接对于降低延迟和提升速度有非常重要的作用。
keepalive机制是什么呢?也就是可以在一次TCP连接中可以持续发送多份数据而不会断开连接。所以连接的多次使用,也就是复用就变得格外重要了,而复用连接就需要对连接进行管理,于是就有了连接池的概念。
OkHttp中使用ConectionPool实现连接池,默认支持5个并发KeepAlive,默认链路生命为5分钟。
怎么实现的?
1)首先,ConectionPool中维护了一个双端队列Deque,也就是两端都可以进出的队列,用来存储连接。
2)然后在ConnectInterceptor,也就是负责建立连接的拦截器中,首先会找可用连接,也就是从连接池中去获取连接,具体的就是会调用到ConectionPool的get方法。
RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
if (connection.isEligible(address, route)) {
streamAllocation.acquire(connection, true);
return connection;
}
}
return null;
}
也就是遍历了双端队列,如果连接有效,就会调用acquire方法计数并返回这个连接。
3)如果没找到可用连接,就会创建新连接,并会把这个建立的连接加入到双端队列中,同时开始运行线程池中的线程,其实就是调用了ConectionPool的put方法。
public final class ConnectionPool {
void put(RealConnection connection) {
if (!cleanupRunning) {
//没有连接的时候调用
cleanupRunning = true;
executor.execute(cleanupRunnable);
}
connections.add(connection);
}
}
其实这个线程池中只有一个线程,是用来清理连接的,也就是上述的cleanupRunnable
private final Runnable cleanupRunnable = new Runnable() {
@Override
public void run() {
while (true) {
//执行清理,并返回下次需要清理的时间。
long waitNanos = cleanup(System.nanoTime());
if (waitNanos == -1) return;
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (ConnectionPool.this) {
//在timeout时间内释放锁
try {
ConnectionPool.this.wait(waitMillis, (int) waitNanos);
} catch (InterruptedException ignored) {
}
}
}
}
}
};
这个runnable会不停的调用cleanup方法清理线程池,并返回下一次清理的时间间隔,然后进入wait等待。
如何清理空闲连接?
源码如下:
long cleanup(long now) {
synchronized (this) {
//遍历连接
for (Iterator i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
//检查连接是否是空闲状态,
//不是,则inUseConnectionCount + 1
//是 ,则idleConnectionCount + 1
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}
idleConnectionCount++;
// If the connection is ready to be evicted, we're done.
long idleDurationNs = now - connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
//如果超过keepAliveDurationNs或maxIdleConnections,
//从双端队列connections中移除
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) { //如果空闲连接次数>0,返回将要到期的时间
// A connection will be ready to evict soon.
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
// 连接依然在使用中,返回保持连接的周期5分钟
return keepAliveDurationNs;
} else {
// No connections, idle or in use.
cleanupRunning = false;
return -1;
}
}
closeQuietly(longestIdleConnection.socket());
// Cleanup again immediately.
return 0;
}
清理的逻辑大致是以下几步:
1.遍历所有的连接,对每个连接调用 pruneAndGetAllocationCount 判断其是否闲置的连接。如果是正在使用中,则直接遍历下一个。
2.对于闲置的连接,判断是否是当前空闲时间最长的。
3.对于当前空闲时间最长的连接,如果其超过了设定的最长空闲时间(5分钟)或者是最大的空闲连接的数量(5个),则清理此连接。否则计算下次需要清理的时间,这样 cleanupRunnable 中的循环变会睡眠相应的时间,醒来后继续清理。
如何判断是否是空闲连接?
其实就是有关刚才说到的一个方法acquire计数方法:
public void acquire(RealConnection connection, boolean reportedAcquired) {
assert (Thread.holdsLock(connectionPool));
if (this.connection != null) throw new IllegalStateException();
this.connection = connection;
this.reportedAcquired = reportedAcquired;
connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
}
在RealConnection中,有一个StreamAllocation虚引用列表allocations。每创建一个连接,就会把连接对应的StreamAllocationReference添加进该列表中,如果连接关闭以后就将该对象移除。
OKHttp的网络缓存
原理
(1)、okhttp的网络缓存是基于http协
(2)、使用DiskLruCache的缓存策略
注意事项:
1、目前只支持GET,其他请求方式需要自己实现。
2、需要服务器配合,通过head设置相关头来控制缓存
3、创建OkHttpClient时候需要配置Cache
缓存流程
1.以Request为key从Cache中读取候选缓存
2.根据「当前时间,Request,候选缓存」构建一个缓存策略,用于判断当前请求是否需要使用网络,是否存在缓存
3.根据缓存策略,如果当前请求不使用网络且没有缓存,直接报错并返回状态码504
4.根据缓存策略,如果当前请求不使用网络且存在缓存,直接返回缓存数据
5.进行网络操作,将请求交给下面的拦截器处理,同时获得返回的Response
6.若通过网络返回的Response的状态码为304,混合缓存Response和网络返回的Response的请求头,更新缓存并返回缓存Response
7.读取网络返回的Response,判断是否需要缓存,如果需要则对Response进行缓存
参考:
[OkHttp 源码解析(一):基本流程]
(https://segmentfault.com/a/1190000012656606#comment-area)