我尝试使用Retrofit(2.1.0)和OkHttp(3.3.1)设置HTTP缓存。我看过很多与这个主题相关的帖子,但都没有帮助。
我写了一些单元测试来查看缓存是如何工作的。它工作正常,但一旦集成在我的应用程序中,魔术就结束了。我将首先向您展示我的实施,然后解释我的一些调查。
首先,这是我的Retrofit实例化:
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient client = httpBuilder
.addNetworkInterceptor(INTERCEPTOR_RESPONSE_SET_CACHE)
.addNetworkInterceptor(INTERCEPTOR_REQUEST_ADD_CHECKSUM)
.addInterceptor(loggingInterceptor)
.cache(cacheHttpClient).build();
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl(BASE_URL)
.build();
这是拦截器添加标头来设置缓存控制:
private final Interceptor INTERCEPTOR_RESPONSE_SET_CACHE = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
response = response.newBuilder()
.header("Cache-Control", "max-age=600") //+ Integer.toString(3600 * 5)
.build();
return response;
}
};
最后一个拦截器添加了2个URL参数:
private static final Interceptor INTERCEPTOR_REQUEST_ADD_CHECKSUM = new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
HttpUrl url = chain.request().url();
url = url.newBuilder().addQueryParameter("rd", "random1").addQueryParameter("chk","check1").build();
Request request = chain.request().newBuilder().url(url).build();
return chain.proceed(request);
}
};
最后,我服务的单一方法:
@Headers("Cache-Control: public, max-stale=500")
@GET("/get_data")
Call<DataResponse> getData(@Query("year") int year, @Query("month") int month, @Query("day") int day);
关于我的调查,我设置了拦截器记录器(应用程序端,而不是网络)以查看发生了什么。我可以看到诸如&#34; Cache-Control之类的行:public,max-stale = 500&#34;在我的日志中。这意味着(至少对我而言)标题应该为OkHttp客户端提供检查缓存的机会。
缓存本身似乎已正确初始化。当我创建它时,我强制初始化并记录缓存中存在的所有URL。以下是它的实现方式:
File httpCacheDirectory = new File(getCacheDir(), "responses");
httpCacheDirectory.getParentFile().mkdirs();
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(httpCacheDirectory, cacheSize);
try {
cache.initialize();
Iterator<String> iterator = cache.urls();
Log.i(TAG, "URLs in cacheHttpClient : ");
while (iterator.hasNext()) {
Log.i(TAG, iterator.next());
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "CACHE NOT INIT");
}
当我使用Wifi启动我的应用程序时,我得到了预期的回复。然后我杀了我的应用程序,禁用Wifi并重新启动应用程序。我希望此时缓存能够提供数据。但它失败了,我只能在日志中看到OkHttp打印的行:
HTTP FAILED:java.net.UnknownHostException:无法解析主机 &#34; my-domain.com&#34;:没有与主机名相关联的地址
最后,在RFC 2616中,可以阅读:
max-stale:表示客户端愿意接受响应 已超过其到期时间。如果为max-stale分配了一个 价值,然后客户愿意接受有的回应 超过其到期时间不超过指定的数量 秒。如果没有为max-stale分配值,那么客户端就是 愿意接受任何年龄的陈旧回应。
当我没有指定一个值时,它实际上是有效的(即使在Wifi关闭时我也会得到响应)。现在,这是我找到的唯一方法#34; work&#34;。所以也许我只是误解了缓存控制指令!?
此时我真的很困惑。我真的希望能够使用OkHttp缓存系统,但不知怎的,我错过了一些东西。
感谢您阅读所有文字!
答案 0 :(得分:1)
使用此方法创建缓存的okkhttpclient
private OkHttpClient createCachedClient(final Context context) {
File httpCacheDirectory = new File(context.getCacheDir(), "cache_file");
Cache cache = new Cache(httpCacheDirectory, 20 * 1024 * 1024);
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setCache(cache);
okHttpClient.interceptors().add(
new Interceptor() {
@Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String cacheHeaderValue = isOnline(context)
? "public, max-age=2419200"
: "public, only-if-cached, max-stale=2419200" ;
Request request = originalRequest.newBuilder().build();
com.squareup.okhttp.Response response = chain.proceed(request);
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", cacheHeaderValue)
.build();
}
}
);
okHttpClient.networkInterceptors().add(
new Interceptor() {
@Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String cacheHeaderValue = isOnline(context)
? "public, max-age=2419200"
: "public, only-if-cached, max-stale=2419200" ;
Request request = originalRequest.newBuilder().build();
com.squareup.okhttp.Response response = chain.proceed(request);
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", cacheHeaderValue)
.build();
}
}
);
return okHttpClient;
}
private boolean isOnline(Context context) {
ConnectivityManager connectivity = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null)
for (int i = 0; i < info.length; i++)
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
return false;
}
调用createCachedClient()方法创建OkHttpClient添加此客户端进行改造
OkHttpClient okHttpClient = createCachedClient(MainActivity.this);
Retrofit retrofit=new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(API)
.addConverterFactory(GsonConverterFactory
.create()).build();
将此权限添加到清单
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
如果互联网第一次可用,它将调用服务并缓存请求,下次最多2419200毫秒,它将使用缓存来响应。即使设备离线,也不会达到2419200毫秒的服务器。