Retrofit 缓存设置,处理 UnKnownHostException 异常信息

2,421 阅读5分钟
原文链接: blog.csdn.net

okHttp是相当于httpURLConnection、HttpClient同一级别的网络请求方式,而现在的httpURLConnection底层也是通过okHttp来实现的。因此square公司之前出了一个新的框架:Retrofit对okHttp近一步封装。

通常我们对网络数据进行请求,联网的时候返回数据,没有网络的时候则无法请求数据。这种效果其实对很多App来说不是很令人满意的。因此,我们希望在没有网络的时候,去调用请求,可以获得历史的缓存数据,这样的话,App也会显得更加人性化,更加的有可使用性。

Retrofit刚好很好的给我们提供了设置缓存的方法。

因为Retrofit是基于okHttp的,因此,它的缓存设置需要通过okHttp来实现。

在这里,给大家两种缓存设置的方法:

1、当请求网络超时,去读取缓存数据:

  1. private static final int TIMEOUT_CONNECT =  5//5秒  
  2.   
  3. static final Interceptor REWRITE_RESPONSE_INTERCEPTOR = new Interceptor() {  
  4.     @Override  
  5.     public okhttp3.Response intercept(Chain chain) throws IOException {  
  6.         //获取retrofit @headers里面的参数,参数可以自己定义,在本例我自己定义的是cache,跟@headers里面对应就可以了  
  7.         String cache = chain.request().header("cache");  
  8.         okhttp3.Response originalResponse = chain.proceed(chain.request());  
  9.         String cacheControl = originalResponse.header("Cache-Control");  
  10.         //如果cacheControl为空,就让他TIMEOUT_CONNECT秒的缓存,本例是5秒,方便观察。注意这里的cacheControl是服务器返回的  
  11.         if (cacheControl == null) {  
  12.             //如果cache没值,缓存时间为TIMEOUT_CONNECT,有的话就为cache的值  
  13.             if (cache == null || "".equals(cache)) {  
  14.                 cache = TIMEOUT_CONNECT + "";  
  15.             }  
  16.             originalResponse = originalResponse.newBuilder()  
  17.                     .header("Cache-Control""public, max-age=" + cache)  
  18.                     .build();  
  19.             return originalResponse;  
  20.         } else {  
  21.             return originalResponse;  
  22.         }  
  23.     }  
  24. };  
	private static final int TIMEOUT_CONNECT = 5; //5秒

	static final Interceptor REWRITE_RESPONSE_INTERCEPTOR = new Interceptor() {
		@Override
		public okhttp3.Response intercept(Chain chain) throws IOException {
			//获取retrofit @headers里面的参数,参数可以自己定义,在本例我自己定义的是cache,跟@headers里面对应就可以了
			String cache = chain.request().header("cache");
			okhttp3.Response originalResponse = chain.proceed(chain.request());
			String cacheControl = originalResponse.header("Cache-Control");
			//如果cacheControl为空,就让他TIMEOUT_CONNECT秒的缓存,本例是5秒,方便观察。注意这里的cacheControl是服务器返回的
			if (cacheControl == null) {
				//如果cache没值,缓存时间为TIMEOUT_CONNECT,有的话就为cache的值
				if (cache == null || "".equals(cache)) {
					cache = TIMEOUT_CONNECT + "";
				}
				originalResponse = originalResponse.newBuilder()
						.header("Cache-Control", "public, max-age=" + cache)
						.build();
				return originalResponse;
			} else {
				return originalResponse;
			}
		}
	};


2、当网络未连接时,直接去读取缓存数据:

  1. <span style="font-size:14px;">  private static  final int TIMEOUT_DISCONNECT = 60 * 60 *  24 * 7//7天      
  2.   
  3. static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {  
  4.         @Override  
  5.         public okhttp3.Response intercept(Chain chain) throws IOException {  
  6.             Request request = chain.request();  
  7.             //离线的时候为7天的缓存。  
  8.             if (JuMuApplication.isWorkingOffline()) {  
  9.                 request = request.newBuilder()  
  10.                         .header("Cache-Control" "public, only-if-cached, max-stale=" + TIMEOUT_DISCONNECT)  
  11.                         .build();  
  12.             }  
  13.             return chain.proceed(request);  
  14.         }  
  15.     };</span>  
	private static final int TIMEOUT_DISCONNECT = 60 * 60 * 24 * 7; //7天	

static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {
		@Override
		public okhttp3.Response intercept(Chain chain) throws IOException {
			Request request = chain.request();
			//离线的时候为7天的缓存。
			if (JuMuApplication.isWorkingOffline()) {
				request = request.newBuilder()
						.header("Cache-Control", "public, only-if-cached, max-stale=" + TIMEOUT_DISCONNECT)
						.build();
			}
			return chain.proceed(request);
		}
	};

使用方法:

首先设置OkhttpClient,为其添加拦截器,然后在设置Retrofit即可。
  1. <span style="font-size:14px;">private static Retrofit retrofit;  
  2. private final static int SIZE_OF_CACHE =  10 * 1024 * 1024// 10 MiB  
  3.   
  4. String cacheFile = context.getCacheDir() + "response_cache";  
  5. Cache cache = new Cache(new File(cacheFile), SIZE_OF_CACHE);  
  6. //利用okhttp实现缓存  
  7. OkHttpClient client = new OkHttpClient.Builder()  
  8. //有网络时的拦截器  
  9. .addNetworkInterceptor(RetrofitCacheStrategy.REWRITE_RESPONSE_INTERCEPTOR)  
  10. //没网络时的拦截器  
  11. .addInterceptor(RetrofitCacheStrategy.REWRITE_RESPONSE_INTERCEPTOR_OFFLINE)  
  12. .cache(cache)  
  13. .build();  
  14. retrofit = new Retrofit.Builder().client(client).baseUrl(BASE_URL).build();</span>  
private static Retrofit retrofit;
private final static int SIZE_OF_CACHE = 10 * 1024 * 1024; // 10 MiB

String cacheFile = context.getCacheDir() + "response_cache";
Cache cache = new Cache(new File(cacheFile), SIZE_OF_CACHE);
//利用okhttp实现缓存
OkHttpClient client = new OkHttpClient.Builder()
//有网络时的拦截器
.addNetworkInterceptor(RetrofitCacheStrategy.REWRITE_RESPONSE_INTERCEPTOR)
//没网络时的拦截器
.addInterceptor(RetrofitCacheStrategy.REWRITE_RESPONSE_INTERCEPTOR_OFFLINE)
.cache(cache)
.build();
retrofit = new Retrofit.Builder().client(client).baseUrl(BASE_URL).build();

关于UnKnownHostException问题解决方法:

当我们首次就去没有网络读取数据的时候,Retrofit会去缓存读取,这时Retrofit认为是有数据可以请求的,但是因为是首次,本地并没有缓存,导致了我们请求服务器的时候,报了UnKnownHostException这个异常。关于这个问题,很多人也在github上面提过,而处理的方法就是直接try catch去避免异常。
当我们用RxJava + Retrofit的时候就更加方便了,我只有在doOnError里对异常进行trycatch或者直接,添加
.onErrorResumeNext(Flowable.<T>empty());
避免所有异常即可,当然后者的方法太笼统了,可能会导致忽略了其他关键问题。