关于Retrofit+OkHttp的强大这里就不多说了,还没了解的同学可以自行去百度。这篇文章主要讲如何利用Retrofit+OkHttp来实现一个较为简单的缓存策略:
即有网环境下我们请求数据时,如果没有缓存或者缓存过期了,就去服务器拿数据,并且将新缓存保存下来,如果有缓存而且没有过期,则直接使用缓存。无网环境下我们请求数据时,缓存没过期则直接使用缓存,缓存过期了则无法使用,需要重新联网获取服务器数据。
缓存处理还是很有必要的,它有效的减少服务器负荷,降低延迟提升用户体验,同时也方便用户即使在没网络的情况下也能使用APP。
之前一直有一个疑惑,既然Retrofit已经是对OkHttp的一个封装了,为什么还一直说Retrofit+OkHttp要一起搭配使用,后来才知道其实OKHttp很重要的一个作用,就是对一些网络请求的配置,例如连接超时,读取超时,以及一些缓存配置等。
一、添加依赖
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
二、配置OkHttpClient(设置缓存路径和缓存文件大小)
1
2
3
4
5
6
7
8
9
10
|
File httpCacheDirectory =
new
File(Environment.getExternalStorageDirectory(),
"HttpCache"
);
//这里为了方便直接把文件放在了SD卡根目录的HttpCache中,一般放在context.getCacheDir()中
int
cacheSize =
10
*
1024
*
1024
;
//设置缓存文件大小为10M
Cache cache =
new
Cache(httpCacheDirectory, cacheSize);
httpClient =
new
OkHttpClient.Builder()
.connectTimeout(
10
, TimeUnit.SECONDS)
//设置连接超时
.readTimeout(
10
, TimeUnit.SECONDS)
//读取超时
.writeTimeout(
10
, TimeUnit.SECONDS)
//写入超时
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
//添加自定义缓存拦截器(后面讲解),注意这里需要使用.addNetworkInterceptor
.cache(cache)
//把缓存添加进来
.build();
|
三、配置Retrofit
1
2
3
4
5
|
retrofit =
new
Retrofit.Builder()
.baseUrl(baseUrl)
.client(httpClient)
//把OkHttpClient添加进来
.addConverterFactory(GsonConverterFactory.create())
.build();
|
四、编写拦截器
我们知道其实Retrofit+OkHttp的缓存主要通过拦截器实现,所以主要做的功夫也在拦截器里面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
static
Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR =
new
Interceptor() {
@Override
public
Response intercept(Chain chain)
throws
IOException {
Request request = chain.request();
//网上很多示例代码都对在request请求前对其进行无网的判断,其实无需判断,无网自动访问缓存
// if(!NetworkUtil.getInstance().isConnected()){
// request = request.newBuilder()
// .cacheControl(CacheControl.FORCE_CACHE)//只访问缓存
// .build();
// }
Response response = chain.proceed(request);
if
(NetworkUtil.getInstance().isConnected()) {
int
maxAge =
60
;
//缓存失效时间,单位为秒
return
response.newBuilder()
.removeHeader(
"Pragma"
)
//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
.header(
"Cache-Control"
,
"public ,max-age="
+ maxAge)
.build();
}
else
{
//这段代码设置无效
// int maxStale = 60 * 60 * 24 * 28; // 无网络时,设置超时为4周
// return response.newBuilder()
// .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
// .removeHeader("Pragma")
// .build();
}
return
response;
}
};
|
到这里,其实已经可以实现了我们开头所说的缓存效果了。
但是,上面设置的每个接口缓存时间都一样,例如我现在想让不同接口的缓存数据失效时间都不一样,甚至有些接口不缓存数据,应该怎么做呢?其实也很简单
首先我们只需要在接口前面添加@Headers参数(max-age代表缓存时间,单位为秒,示例中表示缓存失效时间为60s,想要多少时间可以自行设置),不设置@Headers参数则不进行缓存。
1
2
3
|
@Headers
(
"Cache-Control:public ,max-age=60"
)
@GET
(
"getBusiness.action"
)
//商店信息
Call<RestaurantInfoModel> getRestaurantInfo(
@Query
(
"userId"
) String userId,
@Query
(
"businessId"
) String businessId);
|
同时,我们的缓存拦截器也要做下简单的修改(去掉了之前的注释代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
static
Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR =
new
Interceptor() {
@Override
public
Response intercept(Chain chain)
throws
IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if
(NetworkUtil.getInstance().isConnected()) {
//获取头部信息
String cacheControl =request.cacheControl().toString();
return
response.newBuilder()
.removeHeader(
"Pragma"
)
//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
.header(
"Cache-Control"
, cacheControl)
.build();
}
return
response;
}
};
|
*注意:
1.只能缓存Get请求的接口,不能缓存Post请求的接口
2.OkHttpClient需要用.addNetworkInterceptor添加缓存拦截器,不能使用.addInterceptor,也无需两者同时使用。
3.此方法无需服务器端任何操作,适用于服务器端没有其他缓存策略,如果服务器端有自己的缓存策略代码应该做相应的修改,以适应服务器端。
附上所有代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
/**
* 简单封装的Retroit初始化类
*/
public
class
initRetrofit {
private
static
OkHttpClient httpClient;
private
static
Retrofit retrofit;
public
static
Retrofit initRetrofit() {
//缓存路径和大小
File httpCacheDirectory =
new
File(Environment.getExternalStorageDirectory(),
"HttpCache"
);
int
cacheSize =
10
*
1024
*
1024
;
Cache cache =
new
Cache(httpCacheDirectory, cacheSize);
//日志拦截器
HttpLoggingInterceptor interceptor =
new
HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient =
new
OkHttpClient.Builder()
.connectTimeout(
10
, TimeUnit.SECONDS)
//设置连接超时
.readTimeout(
10
, TimeUnit.SECONDS)
//读取超时
.writeTimeout(
10
, TimeUnit.SECONDS)
//写入超时
.addInterceptor(interceptor)
//添加日志拦截器
.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
//添加缓存拦截器
.cache(cache)
//把缓存添加进来
.build();
retrofit =
new
Retrofit.Builder()
.baseUrl(baseUrl)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
return
retrofit;
}
public
static
RetrofitAPI getService() {
return
initRetrofit().create(RetrofitAPI.
class
);
}
// //缓存拦截器,不同接口不同缓存
// static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
// @Override
// public Response intercept(Chain chain) throws IOException {
//
// Request request = chain.request();
// Response response = chain.proceed(request);
//
// if (NetworkUtil.getInstance().isConnected()) {
// String cacheControl =request.cacheControl().toString();
// return response.newBuilder()
// .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
// .header("Cache-Control", cacheControl)
// .build();
// }
// return response;
// }
// };
//缓存拦截器,统一缓存60s
static
Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR =
new
Interceptor() {
@Override
public
Response intercept(Chain chain)
throws
IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if
(NetworkUtil.getInstance().isConnected()) {
int
maxAge =
60
*
60
*
24
*
2
;
//缓存失效时间,单位为秒
return
response.newBuilder()
.removeHeader(
"Pragma"
)
//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
.header(
"Cache-Control"
,
"public ,max-age="
+ maxAge)
.build();
}
return
response;
}
};
}
搬运自:CKTim
|