OKHTTP中缓存使用理解

一、缓存使用的背景
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缓存中的时间,另外根据缓存的时间值控制不能缓存的场景,比如个人信息。

参考链接
https://blog.csdn.net/foolish0421/article/details/72909861(这篇博客不错,值得参考)
https://blog.csdn.net/qqyanjiang/article/details/51316116(这篇博客不错,值得参考)

猜你喜欢

转载自blog.csdn.net/polo2044/article/details/80650856