addInterceptor与addNetworkInterceptor
addInterceptor
/**
* Returns a modifiable list of interceptors that observe the full span of each call: from
* before the connection is established (if any) until after the response source is selected
* (either the origin server, cache, or both).
*/
public List<Interceptor> interceptors() {
return interceptors;
}
public Builder addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
return this;
}
Application interceptors
- Don’t need to worry about intermediate responses like redirects and retries.
- Are always invoked once, even if the HTTP response is served from the cache.
- Observe the application’s original intent. Unconcerned with OkHttp-injected headers like If-None-Match.
- Permitted to short-circuit and not call Chain.proceed().
- Permitted to retry and make multiple calls to Chain.proceed().
- 不需要担心中间过程的响应,如重定向和重试.
- 总是只调用一次,即使HTTP响应是从缓存中获取.
- 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
- 允许短路(从缓存中获取,不进行网络请求)而不调用 Chain.proceed(),即中止调用.
- 允许重试,使 Chain.proceed()调用多次.
addNetworkInterceptor
/**
* Returns a modifiable list of interceptors that observe a single network request and response.
* These interceptors must call {@link Interceptor.Chain#proceed} exactly once: it is an error
* for a network interceptor to short-circuit or repeat a network request.
*/
public List<Interceptor> networkInterceptors() {
return networkInterceptors;
}
public Builder addNetworkInterceptor(Interceptor interceptor) {
networkInterceptors.add(interceptor);
return this;
}
Network Interceptors
- Able to operate on intermediate responses like redirects and retries.
- Not invoked for cached responses that short-circuit the network.
- Observe the data just as it will be transmitted over the network.
- Access to the Connection that carries the request.
- 能够操作中间过程的响应,如重定向和重试.
- 当网络短路而返回缓存响应时不被调用.
- 只观察在网络上传输的数据.
- 携带请求来访问连接.
Cache
okhttp只会对get请求进行缓存,post请求是不会进行缓存,这也是有道理的,因为get请求的数据一般是比较持久的,而post一般是交互操作,没太大意义进行缓存。
Retrofit2.0+Okhttp3 缓存封装
- 配置权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- 配置okhttp3 cache
@Provides
@Singleton
@HaveCache
OkHttpClient provide CacheOkHttpClient(OkHttpClient.Builder builder) {
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addInterceptor(loggingInterceptor);
}
File cacheFile = new File(Constants.PATH_HTTP_CACHE);
if(!cacheFile.exists()){
cacheFile.mkdirs();
}
Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
builder.addNetworkInterceptor(cacheInterceptor);
builder.addInterceptor(interceptor);
builder.cache(cache);
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
builder.writeTimeout(20, TimeUnit.SECONDS);
builder.retryOnConnectionFailure(true);
return builder.build();
}
- 自定义请求策略
public class CacheTag {
public static final String NO_CACHE = "NO_CACHE";//不缓存服务器数据
public static final String ONLY_CACHE = "ONLY_CACHE";//只使用缓存
public static final String ONLY_NETWORK = "ONLY_NETWORK";//只使用网络
public static final String CACHE_ELSE_NETWORK = "CACHE_ELSE_NETWORK";//先查询本地缓存,没有则从网络获取
public static final String NETWORK_ELSE_CACHE = "NETWORK_ELSE_CACHE";//先通过网络获取,没有则使用本地缓存
}
Retrofit 进行get请求
固定缓存策略
@Headers("cacheTag:缓存策略") @GET("method/test") Flowable<ResultMessage> getXXX(@Query("param1") String param1);
可配置缓存策略
@GET("method/test") Flowable<ResultMessage> getXXX(@Header("cacheTag") String cache,@Query("param1") String param1);
通过拦截器实现缓存策略
private void buildCache(OkHttpClient.Builder builder) {
File cacheFile = new File(Constants.PATH_HTTP_CACHE);
final Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
builder.cache(cache);
Interceptor networkInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
int maxAge = 60 * 60 * 24 * 28;
response.newBuilder().header("Cache-Control", "public,max-age=" + maxAge)
.removeHeader("Pragma")
.build();//默认全部缓存,过期时间为4周。
List<String> cacheTag = request.headers("cacheTag");
if (!ListUtil.isEmpty(cacheTag)) {
for (String tag : cacheTag) {
if (CacheTag.NO_CACHE.equalsIgnoreCase(tag)) {
response.newBuilder().header("Cache-Control", "no-store")
.removeHeader("Pragma")
.build();//修改响应头,不缓存。
}
}
}
return response;
}
};
Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
List<String> cacheTag = request.headers("cacheTag");
if (!ListUtil.isEmpty(cacheTag)) {
for (String tag : cacheTag) {
if (CacheTag.ONLY_CACHE.equalsIgnoreCase(tag)) {//只从缓存中读
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
} else if (CacheTag.ONLY_NETWORK.equalsIgnoreCase(tag)) {//只从网络中获取
if (SystemUtil.isNetworkConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
}else {//若网络不可用直接抛出异常
throw new IOException("网络不可用!");
}
} else if (CacheTag.CACHE_ELSE_NETWORK.equalsIgnoreCase(tag)) {//先从缓存中获取,若无再请求网络
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
response = chain.proceed(request);
Response cacheResponse = response.cacheResponse();
if (cacheResponse == null) {//无缓存的情况,从网络获取
response = null;
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
}
} else if (CacheTag.NETWORK_ELSE_CACHE.equalsIgnoreCase(tag)) {//先进行网络请求,请求失败再获取缓存
if (SystemUtil.isNetworkConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
try {
response = chain.proceed(request);
} catch (Exception e) {//抛出异常,表示网络请求失败,改从本地获取
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE)
.build();
}
} else {//网络不可用,直接从本地获取
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE)
.build();
}
} else if (CacheTag.CACHE_AND_NETWORK.equalsIgnoreCase(tag)) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
Response proceed = chain.proceed(request);
}
}
}
if (response == null) {
response = chain.proceed(request);
}
return response;
}
};
builder.addNetworkInterceptor(networkInterceptor);
builder.addInterceptor(interceptor);
}
no-cache 和 no-store
/**
* In a response, this field's name "no-cache" is misleading. It doesn't prevent us from caching
* the response; it only means we have to validate the response with the origin server before
* returning it. We can do this with a conditional GET.
*
* <p>In a request, it means do not use a cache to satisfy the request.
*/
public boolean noCache() {
return noCache;
}
/** If true, this response should not be cached. */
public boolean noStore() {
return noStore;
}
Cache-Control 常见值
- max-age:这个参数告诉浏览器将页面缓存多长时间,超过这个时间后才再次向服务器发起请求检查页面是否有更新。对于静态的页面,比如图片、CSS、Javascript,一般都不大变更,因此通常我们将存储这些内容的时间设置为较长的时间,这样浏览器会不会向浏览器反复发起请求,也不会去检查是否更新了。
- s-maxage:这个参数告诉缓存服务器(proxy,如Squid)的缓存页面的时间。如果不单独指定,缓存服务器将使用max-age。对于动态内容(比如文档的查看页面),我们可告诉浏览器很快就过时了(max-age=0),并告诉缓存服务器(Squid)保留内容一段时间(比如,s-maxage=7200)。一旦我们更新文档,我们将告诉Squid清除老的缓存版本。
- must-revalidate:这告诉浏览器,一旦缓存的内容过期,一定要向服务器询问是否有新版本。
- proxy-revalidate:proxy上的缓存一旦过期,一定要向服务器询问是否有新版本。
- no-cache:不做缓存。
- no-store:数据不在硬盘中临时保存,这对需要保密的内容比较重要。
- public:告诉缓存服务器, 即便是对于不该缓存的内容也缓存起来,比如当用户已经认证的时候。所有的静态内容(图片、Javascript、CSS等)应该是public的。
- private:告诉proxy不要缓存,但是浏览器可使用private cache进行缓存。一般登录后的个性化页面是private的。
- no-transform: 告诉proxy不进行转换,比如告诉手机浏览器不要下载某些图片。
- max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。