脚本宝典收集整理的这篇文章主要介绍了OkHttp缓存篇,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
addInterceptor
:应用层拦截器,在网络请求前拦截。addNetworkInterceptor
:网络层拦截器,在发起网络请求后,进行拦截。callTimeout
:本次请求的总体超时时间。包括connect、write、read等阶段时间。connectTimeout
:连接阶段超时时间,默认10秒。配置tcp层socket参数,java.net.Socket#connect(java.net.SocketAddress, int)
。参考Okhttp源码RealConnection#connectSocket
。readTimeout
:socket read
函数超时时间,默认10秒。配置tcp层socket参数,java.net.Socket#setSoTimeout
。参考Okhttp源码RealConnection#connectSocket
。writeTimeout
:连接阶段超时时间,默认10秒。// 定义一个拦截器
Interceptor interceptor = chain -> {
Request request = chain.request();
long t1 = System.nanoTime();
LOG.i(TAG, String.format("Send request %s on %s%n%s", request.url(), chain.connection(), request.headers()));
Response response = chain.PRoceed(request);
long t2 = System.nanoTime();
Log.i(TAG, String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
};
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor) // 应用层拦截器,在网络连接前生效
.addNetworkInterceptor(interceptor) // 网络请求前的拦截器,在网络连接后生效
.cache(new Cache(new File("/data/data/com.test.http/cache", "http_cache"), 50 * 1024 * 1024)) // 50 MB
.callTimeout(5, TimeUnit.SECONDS)
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.eventListener(new EventListener(){})
.build();
CacheControl cacheControl = new CacheControl.Builder()
.noCache() // 即使有缓存,每次也都发起网络请求,收到304相应后,使用缓存。否则更新缓存。
.maxAge(60, TimeUnit.SECONDS)
// .onlyIfcached() //
.build();
Request request = new Request.Builder()
.url("https://github.COM/square/okhttp")
.cacheControl()
.build();
client.newCall(request).enqueue(new Callback() {
@override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Log.i(TAG, "onFailure " + call.request());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, "onResponse " + call.request() + ", Response Content" + response);
}
});
@H_297_777@
RealCall#getResponseWithInterceptorChain
这个函数来遍历拦截器,发起网络请求。拦截器是按照添加顺序进行遍历的。client.interceptors()
、RetryAndFollowUpInterceptor
、BridgeInterceptor
、CacheInterceptor
、ConnectInterceptor
、client.networkInterceptors()
、CallServerInterceptor
。okhttp3.Interceptor.Chain
接口的唯一实现类是RealInterceptorChain
。通过该类的RealInterceptorChain#proceed(okhttp3.Request)
实现遍历拦截器的操作。// okhttp3.RealCall#getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); // 自定义应用拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client)); // 重试、重定向拦截器
interceptors.add(new BridgeInterceptor(client.cookiejar())); // 桥接拦截器,主要是修改header参数、gzip压缩
interceptors.add(new CacheInterceptor(client.internalCache())); // 缓存
interceptors.add(new ConnectInterceptor(client)); // 待研究
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors()); // 待研究
}
interceptors.add(new CallServerInterceptor(forWebSocket)); // 待研究
// okhttp3.Interceptor#intercept方法chain参数的实际类型
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
index
的累加,实现对interceptors
的遍历。设计模式的责任链模式
。// okhttp3.internal.http.RealInterceptorChain#proceed(okhttp3.Request, okhttp3.internal.connection.Transmitter, okhttp3.internal.connection.Exchange)
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
// 省略异常检查代码.....
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// 省略对Response的异常检查代码.....
return response;
}
OkHttpClient.Builder#retryOnConnectionFailure(boolean)
RetryAndFollowUpInterceptor#followUpRequest
。做多重定向 20次
// okhttp3.internal.http.BridgeInterceptor#intercept
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decomPressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
CacheInterceptor缓存详解
章节。连接拦截器篇
:https://blog.csdn.net/followYouself/article/details/121086869Okio
向服务器进行交互,进行request
请求,接收response
。Http2
相关的请求类 Http2Stream.java
, Http2Reader
,Http2Writer
。CacheInterceptor
缓存详解OkHttpClient.Builder#cache(@Nullable Cache cache)
方法进行配置,指定缓存File文件路径。CacheControl
一些关键配置no-cache
、no-Store
、max-age
、only-if-cached
、max-stale
。参考下文Http header
章节addNetworkInterceptor
,在Response
中增加cache-control
,配置max-age
(优先级高),Last-Modify
(优先级低)等参数。参考源码CacheStrategy.Factory#computeFreshnessLifetime
。noCache
和noStore
get
、put
、remove
、update
分别对应缓存的读、写、删除、更新操作。diskLruCache
封装了缓存读写的能力,利用了Okio
的读写能力。参考资料:https://blog.csdn.net/zwlove5280/article/details/79916662okhttp3.Cache#key
方法。Header
信息存储在.0
文件,body信息存储在.1
文件,所有操作的日志记录在journal
文件。DiskLruCache
细节没有研究。CacheStrategy.Factory#get()
、CacheStrategy.Factory#getCandidate()
networkRequest
不为空,表示需要发起网络请求。null,则表示不需要网络请求。cacheResponse
不为null,该缓存需要验证或者直接作为结果。为null,表示不使用缓存。networkRequest
不为空,同时request也配置了only-if-cached
,那么会报504
错误, Unsatisfiable Request (only-if-cached)
。CacheStrategy#isCacheable
,首先判断Response
的状态码是否支持缓存,然后在检查Response
和Request
的header,如果配置了no-store
,那么不支持缓存。CacheStrategy.Factory#getCandidate
函数。代码中的注释 1,2,3分别与下面对应cacheResponse
,那么需要网络请求。cacheResponse
中没有handshake
,那么需要网络请求。isCacheable
判断cacheResponse
中的状态码是否支持缓存,同时判断request请求是否配置了noStore
。如果配置了noStore
,那么禁止使用缓存。noCache
或者配置了If-Modified-Since
或者配置了If-None-Match
,那么直接发起网络请求。根据CacheInterceptor.java
代码,网络请求完成后,如果cacheResponse
不为空,并且收到304
状态码,那么使用cacheResponse
作为请求结果。request
的maxage
、min-fresh
、maxStale
,以及根据cacheResponse
的sentRequestatMillis
、receivedResponseAtMillis
、servedDate
、must-revalidate
等配置,计算cacheResponse
是否过期,是否满足本次的request的要求。如果满足,那么直接使用缓存作为网络请求的结果,不发起实际的网络请求。具体的计算细节没研究…ageMillis
:cacheResponse
从生成到现在的耗时; minFreshMillis
:最小新鲜度,距离最终过期的最短时间,request
中配置;freshMillis
:cacheResponse
和request
配置的max-age
中的较小值;maxStaleMillis
在超过max-age
后,仍然可以接受的时间,request
中配置。cacheResponse
是否携带了ETag
、Last-Modified
、Date
等字段,转换为If-None-Match
和If-Modified-Since
字段,添加到request header中。networkRequest
不为空,同时request也配置了only-if-cached
,根据CacheInterceptor.java
代码,会上报504
错误, Unsatisfiable Request (only-if-cached)
。 // okhttp3.internal.cache.CacheStrategy.Factory
/**
* Returns a strategy to satisfy {@code request} using the a cached response {@code response}.
*/
public CacheStrategy get() {
CacheStrategy candidate = getCandidate(); // 根据request请求和缓存中的Response决定后续的网络请求步骤
// 禁止同时有networkRequest 和 onlyIfCached配置。
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) { // 注释8
// We're forbidden From using the network and the cache is insufficient.
return new CacheStrategy(null, null); // 此处为空,会报`504`错误。
}
return candidate;
}
/** Returns a strategy to use assuming the request can use the network. */
private CacheStrategy getCandidate() {
// No cached response.
if (cacheResponse == null) { // 注释1
return new CacheStrategy(request, null);
}
// Drop the cached response if it's missing a required handshake.
if (request.isHttps() && cacheResponse.handshake() == null) { // 注释2
return new CacheStrategy(request, null);
}
// If this response shouldn't have been stored, it should never be used
// as a response source. This check should be redundant as long as the
// PErsistence store is well-behaved and the rules are constant.
if (!isCacheable(cacheResponse, request)) { // 注释3
return new CacheStrategy(request, null);
}
CacheControl requestCaching = request.cacheControl(); // 获取请求中的cacheControl配置
if (requestCaching.noCache() || hasConditions(request)) { // 注释4
return new CacheStrategy(request, null);
}
CacheControl responseCaching = cacheResponse.cacheControl(); // 注释5
long ageMillis = cacheResponseAge(); // 计算缓存从生成开始,已经度过了多长时间
long freshMillis = computeFreshnessLifetime(); // 返回Response配置的max-age,最大可存活的生命时间
if (requestCaching.maxAgeSeconds() != -1) {
// cacheResponse中配置的max-age 和 request中配置的max-age中,取最小的一个值。
// 此处就最终计算出来允许Response有效时间
freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
}
long minFreshMillis = 0;
if (requestCaching.minFreshSeconds() != -1) {
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
}
long maxStaleMillis = 0;
// 计算可以接受的超期时间
if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
}
// 如果缓存没有过期,满足当前request的要求,那么直接使用缓存作为结果。
if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) { // 注释6
Response.Builder builder = cacheResponse.newBuilder();
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection "Response is stale"");
}
long oneDayMillis = 24 * 60 * 60 * 1000L;
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection "Heuristic expiration"");
}
return new CacheStrategy(null, builder.build());
}
// Find a condition to add to the request. If the condition is satisfied, the response body
// will not be transmitted.
String conditionName;
String conditionValue;
if (etag != null) { // 注释7
conditionName = "If-None-Match";
conditionValue = etag;
} else if (lastModified != null) {
conditionName = "If-Modified-Since";
conditionValue = lastModifiedString;
} else if (servedDate != null) {
conditionName = "If-Modified-Since";
conditionValue = servedDateString;
} else {
return new CacheStrategy(request, null); // No condition! Make a regular request.
}
Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue); // 添加到request
Request conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build();
return new CacheStrategy(conditionalRequest, cacheResponse);
}
maxRequests
最大并发请求数量,默认64。maxRequestsPerHost
每个主机host支持的最大并发请求数量,默认5。可配置。executorService
,用于异步的网络请求调度。默认的配置为核心线程为0,最大线程数不限制,存活时间60秒。// okhttp3.Dispatcher#executorService
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
Accept-Encoding:gzip
代表压缩编码数据Connection:Keep-Alive
代表需要长连接no-cache
:资源是可以被客户端缓存的,代理服务器不缓存。但是每次请求都需要先到服务端验证资源是否有效。相关配置参考Last-Modify / If-Modify-Since
no-store
:禁止任何形式的缓存。客户端和服务端均可进行配置生效。max-age
:资源可以被缓存的时间,单位秒。max-age会覆盖掉expires
。超期后,访问服务器校验有效性。s-maxage
:设置代理服务器缓存的最大的有效时间,单位秒。s-maxage
会覆盖掉max-age
。public
:表示当前的请求是一种通用的业务数据,客户端、代理服务器、中间节点服务器都可以缓存这些数据。private
:默认值。表示当前的操作是和具体用户强相关的特殊行为,不应该在代理类服务器、中间节点服务器等进行缓存。因为缓存并没有很大的意义。only-if-cached
:不进行网络请求,完全只使用缓存.如果缓存不命中,返回504错误。并且此处的优先级要高于no-cache
,肯定不会发起网络请求了。must-revalidate
: 资源一旦过期,则必须向服务器发起请求确认资源有效性。如果无法访问服务器,则上报 504 Gateway Timeout。优先级高于max-stale
,设置之后max-stale
变为无效。max-stale
:客户端要求缓存代理该时间内(默认不限时间)的资源无论缓存有没有过期都返回给客户端。这个参数表示业务可以接受的响应的过期时间。no-transform
:缓存代理不可更改媒体类型,这样可以防止压缩图片、压缩资源的操作min-fresh
:距离缓存Response
过期(max-age
、max-stale
之和)剩余的最小时间,保证取到缓存不会在短时间(min-fresh
)内超期无效。比如max-age=100
, max-stale=500
,min-fresh=200
。那么Response
的max-age
+ max-stale
和是600,由于配置了min-fresh
,那么要求在使用缓存是,必须保证距离600这个最终过期时间点,保留200的新鲜度。600 - 200 = 400
,那么也就是缓存的Response
从缓存开始到现在nowTime
最多度过了400的时间长度,才可以使用。Last-Modify/If-Modify-Since
,If-None-Match/ETag
etag
的校验优先级高于Last-Modify
。Last-Modify
:在服务器的Response中携带,表示业务数据上次被修改的时间If-Modify-Since
:当缓存过期时,在客户端request请求时携带,服务器上检查request的请求时间,并校验服务器资源是否被修改。如果被修改,那么返回最新数据,并返回HTTP 200 OK
。如果资源没有修改,那么仅仅返回状态码HTTP 304
etag
:服务器Response携带的资源tag。资源的任何修改都会导致tag的改变,如果tag不变,是可以表示资源数据没有变化的。If-None-Match
:当缓存过期时,客户端request的请求中携带,携带的是缓存中Response的tag值。同样是根据tag判断资源数据是否被修改,相应客户端请求。Mozilla/5.0 (Windows NT 10.0; Win64; x64) Applewebkit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36
OkHttpClient#sslSocketFactory
, 默认配置函数OkHttpClient#newSslSocketFactory
。AndroidPlatform#getSSLContext
,源码如下@Override
public SSLContext getSSLContext() {
boolean tryTls12;
try {
tryTls12 = (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22);
} catch (NoClaSSDefFoundError e) {
// Not a real Android runtime; probably RoboVM or MoE
// Try to load TLS 1.2 explicitly.
tryTls12 = true;
}
if (tryTls12) {
try {
return SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException e) {
// fallback to TLS
}
}
try {
return SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No TLS provider", e);
}
}
帧(frame)
和流(stream)
。每一个流都是有一个独一无二的编号。以上是脚本宝典为你收集整理的OkHttp缓存篇全部内容,希望文章能够帮你解决OkHttp缓存篇所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。