一、缓存使用的背景
APP在网络请求过程中,存在这样几个场景,在断网情况下需要APP页面存在原有网络请求下来的图片和信息;不论网络是否正常,某些后台信息在短时间内都不会被更新了,APP中触发的网络请求不用去后台获取信息,给后台带来压力;用户的信息必须去后台进行更新,获取最新的信息,比如用户的资金方面的信息。这三类场景就涉及到了Http中的网络请求中的缓存,只有了解了Http缓存知识点才能知道对上述三类场景如何进行应对。
二、HTTP 缓存参数说明
HTTP缓存可以分为两类,一类是在request中添加参数,是用来告知服务器;一类是在response中添加参数,用来通知客户端;在设置缓存时需要Get的requset或response的header中添加
Cache-Control:参数。参数的定义有如下:
public
:(仅为响应标头)
reponse:告知任何途径的缓存者,可以无条件的缓存该响应。
private:
(仅为响应标头)
response:告知缓存者, 只针对单个用户缓存响应。且可以具体指定某个字段如private –“username”,则响应头中,名为username的标头内容,不会被共享缓存。
no-cache
:
requset:
告知缓存者,必须原原本本的转发原始请求,并告知任何缓存者,别直接拿你缓存的副本糊弄人。你需要去转发我的请求,并验证你的缓存(如果有的话)。对应名词:端对端重载.
response:允许缓存者缓存副本。那么其实际价值是,总是强制缓存者校验缓存的新鲜度。在确认缓存新鲜的情况下,可以使用缓存副本作为响应。no-cache还可以指定某个包含字段,比如一个典型应用,no-cache=Set-Cookie. 这样做的结果,就是告知缓存者,对于Set-Cookie字段,你不要使用缓存内容.而是使用新滴.其他内容则可以使用缓存.
max-age
:
request:强制响应缓存者,根据该值校验新鲜性,即自身的Age值与请求时间做比较。如果超出max-age值,则强制去服务器端验证,以确保返回一个新鲜的响应。在当max-age=0时,代表强制服务器返回最新的结果。其功能本质上与传统的Expires类似。但区别在于Expires是根据某个特定日期值做比较,一但缓存者自身的时间不准确,则结果可能就是错误的。而max-age,显然无此问题。Max-age的优先级也是高于Expires的。
reponse:指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
三、在OKHTTP中使用缓存
OKHTTP在创建的时候可在拦截机中添加缓存配置,参考代码如下:
OkHttpClient
.Builder builder =
new
OkHttpClient
.Builder();
builder.connectTimeout(
DEFAULT_TIMEOUT
, TimeUnit.
SECONDS
);
builder.writeTimeout(
DEFAULT_TIMEOUT
, TimeUnit.
SECONDS
);
builder.readTimeout(
DEFAULT_TIMEOUT
, TimeUnit.
SECONDS
);
if
(Ln.
isDebugEnabled
()) {
HttpLoggingInterceptor loggingInterceptor =
new
HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.
BASIC
);
builder.interceptors().add(loggingInterceptor);
}
builder.cache(
new
Cache(
new
File(context.getFilesDir(),
"responses"
),
CONST_10
*
CONST_1024
*
CONST_1024
)); //设置缓存存在路径和大小
if
(DevSettingStore.
isDevChannel
()) {
builder.interceptors().add(
new
DevUrlInterceptor(context));
}
builder.interceptors().add(
new
NetStatisticsInterceptor());
// 无网络时,缓存一天
builder.interceptors().add(new LocalCacheInterceptor(context, NetAccessStrategy.
DEFAULT_MAX_LOCAL_CACHE_SECONDS
));
// 有网络时,缓存时间设置
builder.networkInterceptors().add(0, new NetCacheInterceptor(NetAccessStrategy.
DEFAULT_MAX_NET_CACHE_SECONDS
));
builder.build();
|
LocalCacheInterceptor的代码定义如下,在request请求中如果没定义缓存“Cache-Control”字段的情况下,如果网络环境不可以上网,则在网络请求中设置使用缓存的时效,让http请求不用去服务器请求数据,直接使用缓存中的数据就可以了。
package
common.retrofit.interceptor;
import
android.content.Context;
import
android.text.TextUtils;
import
common.retrofit.NetAccessStrategy;
import
common.utils.NetworkUtils;
import
java.io.IOException;
import
java.util.concurrent.TimeUnit;
import
okhttp3.CacheControl;
import
okhttp3.Interceptor;
import
okhttp3.Request;
import
okhttp3.Response;
public class
LocalCacheInterceptor
implements
Interceptor {
private int
maxCacheSeconds
;
private
Context
context
;
public
LocalCacheInterceptor(Context context,
int
maxCacheSeconds) {
this
.
context
= context;
this
.
maxCacheSeconds
= maxCacheSeconds;
}
@Override
public
Response intercept(Chain chain)
throws
IOException {
Request request = chain.request();
String header = request.header(
"Cache-Control"
);
if
(TextUtils.
isEmpty
(header)) {
if
(!NetworkUtils.
isNetworkAvailable
(
context
)) {
Request.Builder builder = request.newBuilder();
//if network not available, load in cache
CacheControl cacheControl = new CacheControl.Builder().maxAge(Integer.
MAX_VALUE
, TimeUnit.
SECONDS
)
.maxStale(maxCacheSeconds, TimeUnit.
SECONDS
).build();
request = builder.cacheControl(cacheControl).build();
return chain.proceed(request);
}
}
else if
(NetAccessStrategy.
NO_CONTROL
.equals(header)) {
Request.Builder builder = request.newBuilder();
if
(NetworkUtils.
isNetworkAvailable
(
context
)) {
builder.removeHeader(
"Cache-Control"
);
}
else
{
//if network not available, load in cache
CacheControl cacheControl = new CacheControl.Builder().maxAge(Integer.
MAX_VALUE
, TimeUnit.
SECONDS
)
.maxStale(maxCacheSeconds, TimeUnit.
SECONDS
).build();
builder.cacheControl(cacheControl);
}
return
chain.proceed(builder.build());
}
else if
(header.startsWith(NetAccessStrategy.
NET_REQUEST
)) {
Request.Builder builder = request.newBuilder();
int
[] timeArray = NetAccessStrategy.
getRequestCacheTime
(header);
CacheControl cacheControl =
new
CacheControl.Builder().maxAge(timeArray[
0
], TimeUnit.
SECONDS
).build();
NetAccessStrategy.
setThreadLocalCacheTime
(timeArray[
1
]);
builder.cacheControl(cacheControl);
return
chain.proceed(builder.build());
}
return
chain.proceed(request);
}
}
|
NetCacheInterceptor的定义如下,承接上面 LocalCacheInterceptor中的逻辑,设置不同的reponse缓存时间。
package
common.retrofit.interceptor;
import
android.text.TextUtils;
import
common.retrofit.NetAccessStrategy;
import
java.io.IOException;
import
okhttp3.Interceptor;
import
okhttp3.Request;
import
okhttp3.Response;
public class
NetCacheInterceptor
implements
Interceptor {
private int
maxCacheSeconds
;
public
NetCacheInterceptor(
int
maxCacheSeconds) {
this
.
maxCacheSeconds
= maxCacheSeconds;
}
@Override
public
Response intercept(Chain chain)
throws
IOException {
Request request = chain.request();
int
time = NetAccessStrategy.
getThreadLocalCacheTime
();
if
(TextUtils.
isEmpty
(request.header(
"Cache-Control"
))) {
Request.Builder builder = request.newBuilder();
request = builder.build();
Response originalResponse = chain.proceed(request);
return
originalResponse.newBuilder().header(
"Cache-Control"
,
"public, max-age="
+
maxCacheSeconds
)
.build();
}
else
{
Request.Builder builder = request.newBuilder();
request = builder.build();
Response originalResponse = chain.proceed(request);
if
(time >
0
) {
return
originalResponse.newBuilder().header(
"Cache-Control"
,
"public, max-age="
+ time)
.build();
}
return
originalResponse.newBuilder().header(
"Cache-Control"
, request.header(
"Cache-Control"
))
.build();
}
}
}
|
缓存逻辑属于之前同事写的逻辑,针对缓存的逻辑编写有点绕,个人的理解是:在有网的环境下会对所有网络请求进行一次缓存,缓存的时间限制为max-age的值,在没有网络的情况下,强制去缓存中去,取的同时更新延长response缓存中的时间,另外根据缓存的时间值控制不能缓存的场景,比如个人信息。
参考链接