网络---OKhttp缓存

缓存详解

来自<https://juejin.im/post/5a6c87c46fb9a01ca560b4d7>

你应该了解的 一些web缓存相关的概念.

来自<http://www.cnblogs.com/_franky/archive/2011/11/23/2260109.html>

HTTP头的Expires与Cache-control

来自<http://www.cnblogs.com/yuyii/archive/2008/10/16/1312238.html>

 

★OKHttp使用Interceptor的缓存问题

来自<https://www.jianshu.com/p/cf59500990c7>

★Okhttp缓存源码分析和自定义缓存实现

来自<https://www.jianshu.com/p/cad8cdfe8b26>

★OkHttp缓存使用指南

来自<https://juejin.im/post/5a60c4866fb9a01ca872075f>

Android okHttp网络请求之缓存控制Cache-Control

来自<http://www.cnblogs.com/whoislcj/p/5537640.html>

 

 

Okhttp3源码(2)---CacheInterceptor解析

 

来自 <https://blog.csdn.net/xiaozhang1993/article/details/76680599>

 

OKhttp源码解析---拦截器之CacheInterceptor

 

来自 <https://blog.csdn.net/new_abc/article/details/53009395>

 

 

设计模式之责任链模式

来自<https://zhuanlan.zhihu.com/p/24737592>

OKHttp中带有缓存处理机制,但是相应的缓存控制,没有响应头,请求头!所以需要通过Intercepter进行拦截,添加请求头以及响应头

使用Intercept在拦截器在请求前后添加缓存请求,Request---app拦截器---cacheInterceptor---Net拦截器

CacheControl   maxAge 缓存使用期限,maxStale 缓存过期时间,no-cache是否与服务器校验内容是否

注意的是:okhttp只会对get请求进行缓存,post请求是不会进行缓存,这也是有道理的,因为get请求的数据一般是比较持久的,而post一般是交互操作,没太大意义进行缓存。

2.进行get请求:

一般来说的是,我们get请求有时有不一样的需求,有时需要进行缓存,有时需要直接从网络获取,有时只获取缓存数据,这些处理,okhttp都有帮我们做了,我们做的只需要设置就是了。下面是整理的各种需求的设置与使用方法。

 

maxAge与maxStale的区别:

maxAge:没有超出maxAge,不管怎么样都是返回缓存数据,超过了maxAge,发起新的请求获取数据更新,请求失败返回缓存数据。

maxStale:没有超过maxStale,不管怎么样都返回缓存数据,超过了maxStale,发起请求获取更新数据,请求失败返回失败

来自<http://blog.csdn.net/u014614038/article/details/51210685>

 

max-stale在请求头设置有效,在响应头设置无效。(因为max-stale是请求头设置参数,参考上面的缓存相关的知识第二个链接)

max-stale和max-age同时设置的时候,缓存失效的时间按最长的算。

关于max-age和max-stale我这里做了一个测试:

测试结果:

我在请求头中设置了:Cache-Control:public, max-age=60,max-stale=120,响应头的Cache-Control和请求头一样。

  • 在第一次请求数据到一分钟之内,响应头有:Cache-Control: public, max-age=60,max-stale=120
  • 在1分钟到3分钟在之间,响应头有:Cache-Control: public, max-age=60,max-stale=120
    Warning: 110 HttpURLConnection "Response is stale"
    可以发现多了一个Warning。
  • 三分钟的时候:重新请求了数据,如此循环,如果到了重新请求的节点此时没有网,则请求失败。

 

 

为了帮助确保将最新鲜的内容返回给客户端应用程序,客户端缓存策略和服务器重新验证要求的交互始终会造成最保守的缓存策略。 本主题中的所有示例阐明针对在1 月 1 日缓存、1 月 4 日过期的资源的缓存策略。

在以下示例中,结合使用了最长过期时间值(maxStale) 与最长使用时间 (maxAge):

  • 如果缓存策略设置 maxAge = 5 天,且未指定 maxStale 值,根据 maxAge 值,此内容在 1 月 6 日前可用。 但是,根据服务器的重新验证要求,内容会在 1 月 4 日过期。 因为内容过期日期更保守(更早),所以它优先于 maxAge 策略。 因此,即使尚未达到最长使用时间,此内容在 1 月 4 日便会过期,并且必须进行重新验证。
  • 如果缓存策略设置 maxAge = 5 天,maxStale = 3 天,根据 maxAge 值,此内容在 1 月 6 日前可用。 根据 maxStale 值,此内容在 1 月 7 日前可用。 因此,会在 1 月 6 日重新验证此内容。
  • 如果缓存策略设置 maxAge = 5 天,maxStale = 1 天,根据 maxAge 值,此内容在 1 月 6 日前可用。 根据 maxStale 值,此内容在 1 月 5 日前可用。 因此,会在 1 月 5 日重新验证此内容。

当内容的最长使用时间小于过期日期时,更保守的缓存行为占据优先级,且最长过期时间值不会有任何效果。 以下示例阐明了在内容过期之前到达最长使用时间(maxAge) 时设置最长过期时间 (maxStale) 值的效果:

  • 如果缓存策略设置 maxAge = 1 天,且未指定 maxStale 值,即使内容尚未过期,也会在 1 月 2 日重新验证此内容。
  • 如果设缓存策略设置 maxAge = 1 天,maxStale = 3 天,会在 1 月 2 日重新验证此内容以强制实施更保守的策略设置。
  • 如果缓存策略设置 maxAge = 1 天,maxStale = 1 天,会在 1 月 2 日重新验证此内容。

来自 <https://docs.microsoft.com/zh-cn/dotnet/framework/network-programming/cache-policy-interaction-maximum-age-and-maximum-staleness>

 

 

==================================================================

一、环境的搭建和集成

1.先集成okhttp和retrofit我就直接贴出来我的项目里面的了。

compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.okio:okio:1.6.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

这样配置就可以了。

2.最简单的使用我就不说了。直接调用了。

  1. OkHttpClient.Builder builder = new OkHttpClient.Builder();  
  2.    
  3.         OkHttpClient client = new OkHttpClient.Builder()  
  4.                 .retryOnConnectionFailure(true)//连接失败后是否重新连接  
  5.                 .connectTimeout(15, TimeUnit.SECONDS)//超时时间15S  
  6.                 .build();  
  7.   
  8.         Retrofit retrofit = new Retrofit  
  9.                 .Builder()  
  10.                 .baseUrl(AppConstants.RequestPath.HOST)  
  11.                 .client(client)//设置okhttp  
  12.                 .addConverterFactory(GsonConverterFactory.create(new Gson()))//解析数据  
  13.                 .build();  
  14.         server = retrofit.create(ApiServer.class);  

 

那么这里就是我们最简单的调用了有了server就很简单了啦。直接调用Api里面的自己编写的接口。

对于okhtt+retrofit不会使用的大家可以去看看帖子啊。这里就过多讲了啊。主要讲缓存策略的问题。

 

二、缓存实现

OkHttp拦截器:

OkHttp默认对Http缓存进行了支持,只要服务端返回的Response中含有缓存策略(不一定有,所以需要拦截器添加),OkHttp就会通过CacheInterceptor拦截器对其进行缓存。但是OkHttp默认情况下构造的HTTP请求中并没有加Cache-Control,即便服务器支持了,我们还是不能正常使用缓存数据。所以需要对OkHttp的缓存过程进行干预,使其满足我们的需求。

这里就说到了我们的两种缓存:

一、无论有无网络我们都去获取缓存的数据(我们会设置一个缓存时间,在某一段时间内(例如60S)去获取缓存数据。超过60S我们就去网络重新请求数据)

二、有网络的时候我们就去直接获取网络上面的数据。当没有网络的时候我们就去缓存获取数据。

 

返回Response时按照逆序执行:

自定义拦截器 <->内置拦截器(retryAndFollowUpInterceptor...ConnectInterceptor)<-> 网络拦截器<-> CallServerInterceptor

 

Request<-> 自定义拦截器 <-> CacheInterceptor <-> 网络拦截器 <-> Response

如果服务端返回的Response中没有Cache-Control,那么我们可通过添加网络拦截器来实现。

 

注意:在无网络的情况下,请求在执行到CacheIntercepter,如果没有缓存数据,将会直接返回,并不会执行到自定义的网络拦截器中,所以不适合在网络拦截器中缓存数据。

所以需要添加两个拦截器

.addInterceptor(new BaseInterceptor(context))       处理request..header("Cache-Control","only-if-cached, max-stale=" + 30 * 24 * 60 * 60);

.addNetworkInterceptor(newHttpCacheInterceptor(context))

缓存的作用:

1.处理高并发的问题:当我们的用户量比较大的时候我们的服务器有时候可能受不了所以我们要把那些并不是经常更新和不是很重要的信息缓存下来。我们只需要去请求那些主要的信息。还有就是有时候我们很无聊就是在app界面刷新刷新刷新,这样的话我们就可以不用把这个看成有效的请求。这时候我们就可以将缓存到本地的数据展示出来,这样的话我们就不用给服务器压力了。

2。这个就是我么没有网络的情况下要显示的缓存。我们有的需求就是我们没有网络的时候我们要展示我们上次显示的信息。这样的话我么就要用到我们的缓存了。

来自 <https://blog.csdn.net/u010286855/article/details/52608485>

 

缓存实现思路:

Rtrofit自己是不支持缓存的,要做缓存用的是okhttp拦截器的功能,响应请求之后保持缓存数据。(注意只能"GET"请求)

okhtt添加拦截器有两种1.addInterceptor,和2.addNetworkInterceptor。

主要是拦截操作,包括控制缓存的最大生命值,控制缓存的过期时间,两个操作都是在Interceptor 中进行的。

addNetworkInterceptor添加的是网络拦截器,他会在在request和resposne是分别被调用一次,

addinterceptor添加的是aplication拦截器,他只会在response被调用一次。

 

缓存的使用情况:

  • 分别需要对Request以及Response进行设置才能达到效果,
    • Request发出请求,设置请求头,使得服务器进行缓存(没有设置的可以即为有网的时候不进行缓存)
    • Response响应设置"cache-control",设置App保存缓存。(因云端有可能没有处理响应头,默认返回cache-control是no-cache,不保存缓存)
  • 可以单纯通过Intercept进行设置
  • 可以通过添加使用CacheControl,CacheIntercept实现

 

情景一、无论有无网络我们都先获取缓存的数据。单独AddNetInterceptor

(我们会设置一个缓存时间,在某一段时间内(例如60S)去获取缓存数据。超过60S我们就去网络重新请求数据)

1.准备缓存保存文件

  1. File cacheFile = new File(BaseApp.getInstance().getCacheDir(), "caheData");  
  2.  //设置缓存大小  
  3. Cache cache = new Cache(cacheFile, DEFAULT_DIR_CACHE);//google建议放到这里  

 

2.创建拦截器。

  1. /** 
  2.      * 一、无论有无网路都添加缓存。 
  3.      * 目前的情况是我们这个要addNetworkInterceptor 
  4.      * 这样才有效。经过本人测试(chan)测试有效. 
  5.      * 60S后如果没有网络将获取不到数据,显示连接失败 
  6.      */  
  1.     static Interceptor netInterceptor = new Interceptor() {  
  2.         @Override  
  3.         public Response intercept(Chain chain) throws IOException {  
  4.             Request request = chain.request();  
  5.             Response response = chain.proceed(request);  
  6.           /*String cacheControl = request.header("Cache-Control"); 
  1.             if (TextUtils.isEmpty(cacheControl)) { 
  2.                 cacheControl = "public, max-age=60"; 
  3.             }*/  
  1.             int maxAge = 60;  
  2.             return response.newBuilder()  
  3.                     .removeHeader("Pragma")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效  
  4.                     .removeHeader("Cache-Control")  
  5.                     .header("Cache-Control""public, max-age=" + maxAge)  
  6.                     .build();  
  7.         }  
  8.     };  

 

3.把拦截器,设置到okhttp里面

  1. OkHttpClient client = new OkHttpClient.Builder()  
  2.                .retryOnConnectionFailure(true)//连接失败后是否重新连接  
  3.                .connectTimeout(15, TimeUnit.SECONDS)//超时时间15S  
  4.                .addNetworkInterceptor(cacheInterceptor)//这里大家一定要注意了是addNetworkOnterceptor别搞错了啊。  
  5.                .cache(cache)  
  6.                .build();  

 

3.良心图好了这样的话第一种缓存策略就写好了我测试下截图发来,别说我吹逼的。

绝对良心截图

 

 

 

我的操作了4次服务器之打印了一次也就是我们在60S只能无论我怎么点击只访问了一次数据。第一次是有网络的时候我获取了数据缓存了。第二次也是有网络的时候我获取的还是缓存的数据。第三次是我段网络后的数据还是有获取的缓存。第四次是我没有网络了,但是超过了设置的60S所以显示连接服务器失败。这样良心的截图说明我想够了吧。还有一点就算是有网络超过了60S我们获取的数据不就是缓存里面的数据了。第一种缓存比较简单也很好配置。

情景二、有网络的时候我们获取网络的数据或者自己设置一定的缓存,没有网络的时候我们获取缓存的数据。

注意:在设置addNetworkInterceptor的基础上,需要设置addInterceptor,实际上就是覆盖重写一个Interceptor,代码中判断在没有网络时,通过劫持request,从而达到目的

必须两个拦截器都加:

OkHttpClient.Builderbuilder =new OkHttpClient.Builder();

builder.addInterceptor(baseInterceptor);

builder.addNetworkInterceptor(rewriteCacheControlInterceptor);

 

 

 

1.分别对Request,Response的缓存策略进行设置。 

Request:无网络时获取缓存

Response:有网络时不进行缓存,无网络缓存

 

  1.  public class CacheInterceptor implements Interceptor {  
  2.         @Override  
  3.         public Response intercept(Chain chain) throws IOException {  
  4.             //获取请求,设置request  无网络情况下获取缓存内容
  5.             Request request = chain.request();
  6.             //这里就是说判读我们的网络条件,要是有网络的话我么就直接获取网络上面的数据,要是没有网络的话我么就去缓存里面取数据  
  7.             if(!NetworkUtils.isNetworkAvailable(BaseApp.getInstance().getApplicationContext())){  
  8.                 request = request.newBuilder()  
  9.                         //这个的话内容有点多啊,大家记住这么写就是只从缓存取,想要了解这个东西我等下在  
  10.                         // 给大家写连接吧。大家可以去看下,获取大家去找拦截器资料的时候就可以看到这个方面的东西反正也就是缓存策略。  
  11.                         .cacheControl(CacheControl.FORCE_CACHE)  
  12.                         .build();  
  13.                 Log.d("CacheInterceptor","no network");  
  14.             }  
  15.            //设置Response,是否保存缓存
  16.             Response originalResponse = chain.proceed(request);  
  17.             if(NetworkUtils.isNetworkAvailable(BaseApp.getInstance().getApplicationContext())){  
  18.                 //这里大家看点开源码看看.header .removeHeader做了什么操作很简答,就是的加字段和减字段的。  
  19.                 String cacheControl = request.cacheControl().toString();  
  20.                 return originalResponse.newBuilder()  
  21.                         //这里设置的为0就是说不进行缓存,我们也可以设置缓存时间  
  22.                         .header("Cache-Control""public, max-age=" + 0)  
  23.                         .removeHeader("Pragma")  
  24.                         .build();  
  25.             }else{  
  26.                 int maxTime = 4*24*60*60;  
  27.                 return originalResponse.newBuilder()  
  28.                         //这里的设置的是我们的没有网络的缓存时间,想设置多少就是多少。  
  29.                         .header("Cache-Control""public, only-if-cached, max-stale="+maxTime)  
  30.                         .removeHeader("Pragma")  
  31.                         .build();  
  32.             }  
  33.         }  
  34.     }  

 

 

2.两种类型的拦截器都设置上缓存拦截器!

  .addInterceptor(new CacheInterceptor())//也就这里不同  

 .addNetworkInterceptor(new CacheInterceptor())//也就这里不同  

 

  1. OkHttpClient.Builder builder = new OkHttpClient.Builder();  
  2.         //设置缓存路径  
  3.         File cacheFile = new File(BaseApp.getInstance().getCacheDir(), "caheData");  
  4.         //设置缓存大小  
  5.         Cache cache = new Cache(cacheFile, DEFAULT_DIR_CACHE);  
  6.         OkHttpClient client = new OkHttpClient.Builder()  
  7.                 .retryOnConnectionFailure(true)//连接失败后是否重新连接  
  8.                 .connectTimeout(15, TimeUnit.SECONDS)//超时时间15S  
  9.                 .readTimeout(15,TimeUnit.SECONDS)
  10.                 .writeTimeout(15,TimeUnit.SECONDS)
  11.                 .addInterceptor(new CacheInterceptor())//也就这里不同  
  12.                 .addNetworkInterceptor(new CacheInterceptor())//也就这里不同  
  13.                 .cache(cache)  
  14.                 .build();  
  15.   
  16.         Retrofit retrofit = new Retrofit  
  17.                 .Builder()  
  18.                 .baseUrl(AppConstants.RequestPath.HOST)//baseURL提倡以“/”结尾  
  19.                 .client(client)//设置okhttp  
  20.                 .addConverterFactory(GsonConverterFactory.create(new Gson()))//解析数据  
  21.                 .build();  
  22.         server = retrofit.create(ApiServer.class);  

大家看看也就是添加拦截器的一点点地方不同。我良心截图了。

 

 

我第一次操作是有网络的时候很明显是最新数据,第二次也是有网络的时候我没有设置缓存数据,这样的话我服务器又收到了相应,后面几次都是没有网络的时候我的数据就是获取的缓存的数据。

 

三、补充,通过设置CacheControl控制缓存数据

  • 通过 CacheControl 控制缓存数据
  1. CacheControl.Builder cacheBuilder = new CacheControl.Builder();
  2.  cacheBuilder.maxAge(0, TimeUnit.SECONDS);//这个是控制缓存的最大生命时间
  3.  cacheBuilder.maxStale(365,TimeUnit.DAYS); //缓存过时时间
  4.  CacheControl cacheControl = cacheBuilder.build();

 

.maxAge(0,TimeUnit.SECONDS)设置的时间比拦截器长是不起效果,如果设置比拦截器设置的时间短就会以这个时间为主。

没有超出maxAge,不管怎么样都是返回缓存数据,超过了maxAge,发起新的请求获取数据更新,请求失败返回缓存数据。

.maxStale(365,TimeUnit.DAYS)设置的是过时时间,

没有超过maxStale,不管怎么样都返回缓存数据,超过了maxStale,发起请求获取更新数据,请求失败返回失败。

我觉得okthhp缓存分成了两个来考虑,一个是为了请求时直接拿缓存省流量,一个是为了下次进入应用时可以直接拿缓存。

 

  • 设置拦截器
  1. Request request = chain.request();
  2. if(!StateUtils.isNetworkAvailable(MyApp.mContext)){
  3.     request = request.newBuilder()
  4.             .cacheControl(cacheControl)
  5.             .build();
  6.  
  7. Response originalResponse = chain.proceed(request);
  8. if (StateUtils.isNetworkAvailable(MyApp.mContext)) {
  9.     int maxAge = 60;
  10.     return originalResponse.newBuilder()
  11.             .removeHeader("Pragma")
  12.             .header("Cache-Control", "public ,max-age=" + maxAge)
  13.             .build();
  14.   } else {
  15.     int maxStale = 60 * 60 * 24 * 28;
  16.     return originalResponse.newBuilder()
  17.             .removeHeader("Pragma")
  18.             .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
  19.             .build();
  20. }

 

  • 完整代码
  1. weiBoApiRetrofit() {
  2.     File httpCacheDirectory = new File(MyApp.mContext.getCacheDir(), "responses");
  3.     int cacheSize = 10 * 1024 * 1024;
  4.     Cache cache = new Cache(httpCacheDirectory, cacheSize);  //准备缓存文件
  5.     OkHttpClient client = new OkHttpClient.Builder()
  6.             .addInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR) //添加拦截器
  7.             .cache(cache).build();
  8.     Retrofit retrofit = new Retrofit.Builder()
  9.             .baseUrl(BASE_URL)
  10.             .client(client)
  11.             .addConverterFactory(GsonConverterFactory.create())
  12.             .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
  13.             .build();
  14.   WeiBoApiService = retrofit.create(WeiBoApi.class);
  15.   Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = chain -> {
  16.       CacheControl.Builder cacheBuilder = new CacheControl.Builder();
  17.       cacheBuilder.maxAge(0, TimeUnit.SECONDS);
  18.       cacheBuilder.maxStale(365,TimeUnit.DAYS);
  19.       CacheControl cacheControl = cacheBuilder.build(); //缓存控制器,不需要也行自定义
  20.       Request request = chain.request();
  21.       if(!StateUtils.isNetworkAvailable(MyApp.mContext)){
  22.           request = request.newBuilder()
  23.                   .cacheControl(cacheControl)
  24.                   .build();
  25.       Response originalResponse = chain.proceed(request);
  26.       if (StateUtils.isNetworkAvailable(MyApp.mContext)) {
  27.           int maxAge = 0;
  28.           return originalResponse.newBuilder()
  29.                   .removeHeader("Pragma")
  30.                   .header("Cache-Control", "public ,max-age=" + maxAge)
  31.                   .build();
  32.       } else {
  33.           int maxStale = 60 * 60 * 24 * 28;
  34.           return originalResponse.newBuilder()
  35.                   .removeHeader("Pragma")
  36.                   .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
  37.                   .build();
  38. }

 

  • 缓存是在每一次网络请求之后,重新保存的,所以在超过缓存过期时间后,Retrofit会在检查到没缓存之后自动请求网络服务器数据,这里要自己处理好后续的操作,比如弹个吐司什么的告诉用户没有网络了。
  • 缓存数据也是需要网络下载的,所以在网络不好的情况下,可能不能立即缓存,这也是我之前犯晕的地方:明明已经设置好缓存了,为什么有时候有缓存,有时候没有呢?- -真是对自己的智商捉急。

来自 <https://juejin.im/entry/57db61190e3dd900695c6357>

 

 

四、Retrofit中设置缓存

 

五、注意点

1.Response的缓存策略进行修改的拦截器一定要应用于网络拦截器,否则无法缓存数据,因为在Response返回的过程中,普通的拦截器在内置的CacheInterceptor之后执行;

2.修改Response的Cache-Control时,max-Age不能太大,否则你将在指定的max-Age时间内访问的始终是缓存数据(即便是有网的情况下);

3.实际的开发过程中,我们在网络请求中会添加一些公共参数,对于一些可变的公共参数,在缓存数据和访问缓存数据的过程中需要删除,比如网络类型,有网络时其值为Wifi或4G等,无网络时可能为none,这时访问缓存时就会因url不一致导致访问缓存失败。

@Override // BaseInterceptor.java

public Response intercept(Chainchain) throws IOException {

 // 添加公共参数

 HttpUrl.Builder urlBuilder =chain.request().url().newBuilder()

                 .addQueryParameter("a","a")

                 .addQueryParameter("b","b");

 Request.Builder requestBuilder =chain.request().newBuilder();

 if (NetworkUtil.isConnected(mContext)) {

    urlBuilder.addQueryParameter("network",NetworkUtil.getNetwokType(mContext));

 } else { // 无网络时不添加可变的公共参数

    requestBuilder.removeHeader("Pragma")

                         .header("Cache-Control","only-if-cached, max-stale=" + 30 * 24 * 60 * 60);

 }

 Request newRequest = requestBuilder

                 .url(urlBuilder.build())

                 .build();

 return chain.proceed(newRequest);

}

 

@Override //HttpCacheInterceptor.java

public Response intercept(Chainchain) throws IOException {

 Response response =chain.proceed(chain.request());

 HttpUrl newUrl =chain.request().url().newBuilder()

                         .removeAllQueryParameters("network")

                         .build();// 缓存数据前删除可变的公共参数

 Request newRequest =chain.request().newBuilder()

                 .url(newUrl)

                 .build();

 return response.newBuilder()

                 .request(newRequest)

                 .removeHeader("Pragma")

                 .header("Cache-Control","public, max-age=" + 1)

                 .build();

}

来自: https://juejin.im/post/5a60c4866fb9a01ca872075f

 

http://www.oschina.NET/news/41397/web-cache-knowledge  这个是缓存的。1.缓存原理2.拦截器3retrofit+okhttp+Rxjava这样的教学一大堆。大家可以去看看。

来自<http://blog.csdn.net/zhangyalong_android/article/details/72733577

阅读更多
个人分类: android
上一篇网络---OkHttp使用
下一篇网络----Volley
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭