okhttp3.4.1+retrofit2.1.0实现离线缓存的示例
关于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(设置缓存路径和缓存文件大小)
FilehttpCacheDirectory=newFile(Environment.getExternalStorageDirectory(),"HttpCache");//这里为了方便直接把文件放在了SD卡根目录的HttpCache中,一般放在context.getCacheDir()中 intcacheSize=10*1024*1024;//设置缓存文件大小为10M Cachecache=newCache(httpCacheDirectory,cacheSize); httpClient=newOkHttpClient.Builder() .connectTimeout(10,TimeUnit.SECONDS)//设置连接超时 .readTimeout(10,TimeUnit.SECONDS)//读取超时 .writeTimeout(10,TimeUnit.SECONDS)//写入超时 .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加自定义缓存拦截器(后面讲解),注意这里需要使用.addNetworkInterceptor .cache(cache)//把缓存添加进来 .build();
三、配置Retrofit
retrofit=newRetrofit.Builder() .baseUrl(baseUrl) .client(httpClient)//把OkHttpClient添加进来 .addConverterFactory(GsonConverterFactory.create()) .build();
四、编写拦截器
我们知道其实Retrofit+OkHttp的缓存主要通过拦截器实现,所以主要做的功夫也在拦截器里面。
staticInterceptorREWRITE_CACHE_CONTROL_INTERCEPTOR=newInterceptor(){ @Override publicResponseintercept(Chainchain)throwsIOException{ Requestrequest=chain.request(); //网上很多示例代码都对在request请求前对其进行无网的判断,其实无需判断,无网自动访问缓存 //if(!NetworkUtil.getInstance().isConnected()){ //request=request.newBuilder() //.cacheControl(CacheControl.FORCE_CACHE)//只访问缓存 //.build(); //} Responseresponse=chain.proceed(request); if(NetworkUtil.getInstance().isConnected()){ intmaxAge=60;//缓存失效时间,单位为秒 returnresponse.newBuilder() .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 .header("Cache-Control","public,max-age="+maxAge) .build(); }else{ //这段代码设置无效 //intmaxStale=60*60*24*28;//无网络时,设置超时为4周 //returnresponse.newBuilder() //.header("Cache-Control","public,only-if-cached,max-stale="+maxStale) //.removeHeader("Pragma") //.build(); } returnresponse; } };
到这里,其实已经可以实现了我们开头所说的缓存效果了。
但是,上面设置的每个接口缓存时间都一样,例如我现在想让不同接口的缓存数据失效时间都不一样,甚至有些接口不缓存数据,应该怎么做呢?其实也很简单
首先我们只需要在接口前面添加@Headers参数(max-age代表缓存时间,单位为秒,示例中表示缓存失效时间为60s,想要多少时间可以自行设置),不设置@Headers参数则不进行缓存。
@Headers("Cache-Control:public,max-age=60") @GET("getBusiness.action")//商店信息 CallgetRestaurantInfo(@Query("userId")StringuserId,@Query("businessId")StringbusinessId);
同时,我们的缓存拦截器也要做下简单的修改(去掉了之前的注释代码)
staticInterceptorREWRITE_CACHE_CONTROL_INTERCEPTOR=newInterceptor(){ @Override publicResponseintercept(Chainchain)throwsIOException{ Requestrequest=chain.request(); Responseresponse=chain.proceed(request); if(NetworkUtil.getInstance().isConnected()){ //获取头部信息 StringcacheControl=request.cacheControl().toString(); returnresponse.newBuilder() .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 .header("Cache-Control",cacheControl) .build(); } returnresponse; } };
*注意:
1.只能缓存Get请求的接口,不能缓存Post请求的接口
2.OkHttpClient需要用.addNetworkInterceptor添加缓存拦截器,不能使用.addInterceptor,也无需两者同时使用。
3.此方法无需服务器端任何操作,适用于服务器端没有其他缓存策略,如果服务器端有自己的缓存策略代码应该做相应的修改,以适应服务器端。
附上所有代码:
/** *简单封装的Retroit初始化类 */ publicclassinitRetrofit{ privatestaticStringbaseUrl="http://202.171.212.154:8080/hh/"; privatestaticOkHttpClienthttpClient; privatestaticRetrofitretrofit; publicstaticRetrofitinitRetrofit(){ //缓存路径和大小 FilehttpCacheDirectory=newFile(Environment.getExternalStorageDirectory(),"HttpCache"); intcacheSize=10*1024*1024; Cachecache=newCache(httpCacheDirectory,cacheSize); //日志拦截器 HttpLoggingInterceptorinterceptor=newHttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient=newOkHttpClient.Builder() .connectTimeout(10,TimeUnit.SECONDS)//设置连接超时 .readTimeout(10,TimeUnit.SECONDS)//读取超时 .writeTimeout(10,TimeUnit.SECONDS)//写入超时 .addInterceptor(interceptor)//添加日志拦截器 .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加缓存拦截器 .cache(cache)//把缓存添加进来 .build(); retrofit=newRetrofit.Builder() .baseUrl(baseUrl) .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); returnretrofit; } publicstaticRetrofitAPIgetService(){ returninitRetrofit().create(RetrofitAPI.class); } ////缓存拦截器,不同接口不同缓存 //staticInterceptorREWRITE_CACHE_CONTROL_INTERCEPTOR=newInterceptor(){ //@Override //publicResponseintercept(Chainchain)throwsIOException{ // //Requestrequest=chain.request(); //Responseresponse=chain.proceed(request); // //if(NetworkUtil.getInstance().isConnected()){ //StringcacheControl=request.cacheControl().toString(); //returnresponse.newBuilder() //.removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 //.header("Cache-Control",cacheControl) //.build(); //} //returnresponse; //} //}; //缓存拦截器,统一缓存60s staticInterceptorREWRITE_CACHE_CONTROL_INTERCEPTOR=newInterceptor(){ @Override publicResponseintercept(Chainchain)throwsIOException{ Requestrequest=chain.request(); Responseresponse=chain.proceed(request); if(NetworkUtil.getInstance().isConnected()){ intmaxAge=60*60*24*2;//缓存失效时间,单位为秒 returnresponse.newBuilder() .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效 .header("Cache-Control","public,max-age="+maxAge) .build(); } returnresponse; } }; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。