okhttp和retrofit实现网络缓存

一、生活中的例子

在APP的使用过程中,类似的新闻客户端,我们会发现无网络的情况下打开APP首页是显示新闻列表的,也就是说此时的数据是缓存数据,那么我们如何也实现这样的效果呢?

二、分析需求

  • 有网络的情况下总是去访问服务器获取最新的数据,并把数据缓存到本地
  • 无网络的情况下直接去缓存里找数据,如果没有找到就抛出异常(我们可以捕获该异常给用户友好提示如当前没有网络连接)

那么会有人疑问,难道所有的接口都是这样的流程吗,比如我有的接口就是不想缓存,无网络的情况下就是要去访问服务器,大不了抛出一个网络连接超时的异常,其实这可以实现的,一会讲到

三、如何实现(注意这里只能缓存get请求接口数据)

1.其实网上有很多实现缓存的文章,主要就是用到了okhttp的拦截器,通过修改请求头的Cache-Control来实现缓存
2.关于okhttp的拦截器我们用到了两个方法addInterceptor和addNetworkInterceptor,网上都到两者的区别都是比较官方的不太好理解,当然我也不是特别清楚,说说我的理解吧

  • addInterceptor优先addNetworkInterceptor执行
  • 有网络的情况下addInterceptor和addNetworkInterceptor都会执行
  • 没有网络的情况下addInterceptor执行,而addNetworkInterceptor不会执行
  • 没有网络但是有缓存的情况下addInterceptor会执行,而addNetworkInterceptor不会执行

3.代码实现

//先进行缓存目录的配置
File httpCacheDirectory = new File(context.getCacheDir(), "OkHttpCache");
int cacheSize = 10 * 1024 * 1024;//设置缓存文件大小为10M
Cache cache = new Cache(httpCacheDirectory, cacheSize);

//和addInterceptor配置同级
mOkHttpClient.cache(cache)
//requestInterceptor 这个拦截器通过addInterceptor添加到OkHttpClient中
Interceptor requestInterceptor = new Interceptor() {
    
    
            @NotNull
            @Override
            public Response intercept(@NotNull Chain chain) throws IOException {
    
    
                Request request = chain.request();
                //拦截请求头 判断哪些接口配置上了缓存(该配置是在retrofit上配置)
                String cacheControl =request.cacheControl().toString();
                //如果没有配置过 那么就是走正常的访问,这里直接return回去了,并没有对请求做处理
                if(TextUtils.isEmpty(cacheControl)) {
    
    
                    return chain.proceed(request);
                }
                //如果没有网络的情况下,并且配置了缓存,那么我们就设置强制读取缓存,没有读到就抛异常,但是并不会去服务器请求数据
                if (!isNetworkConnected(context)) {
    
    
                    int offlineCacheTime = 30;
                    request = request.newBuilder()
                    //only-if-cached 强制使用缓存
                    //max-stale 跟max-age同样用,只是这两个谁的值大,就用谁的值
                            .header("Cache-Control", "public, only-if-cached, max-stale=" + offlineCacheTime)
                            .build();
                }
                return chain.proceed(request);
            }
        };
//responseInterceptor 这个拦截器通过addNetworkInterceptor添加到OkHttpClient中
Interceptor responseInterceptor = new Interceptor() {
    
    
            @NotNull
            @Override
            public Response intercept(@NotNull Chain chain) throws IOException {
    
    
                Request request = chain.request();
                Response response = chain.proceed(request);

                String cacheControl =request.cacheControl().toString();
                //同理获取请求头看看有没有配置过缓存,如果没有配置过就设置成无缓存
                if (TextUtils.isEmpty(cacheControl) || "no-store" .contains(cacheControl)) {
    
    
                    //响应头设置成无缓存
                    cacheControl = "no-store";
                }
                return response.newBuilder()
                //移除掉Pragma 避免服务器默认返回的Pragma对我们自己的缓存配置产生影响
                        .removeHeader("Pragma")
                        .header("Cache-Control", cacheControl)
                        .build();
            }
        };
//跟平时retorfit写法一样,只是添加了@Headers这个配置
@Headers("Cache-Control:public ,max-age=0")
@GET("user/account/getStudent/v1.0")
Observable<BaseResBean<UserBean>> getUserInfo(@QueryMap HashMap<String, Object> map);

四、如何实现的缓存

其实上面的四部分代码就已经实现了我们想要的需求,那么接下来分析一下是如何实现的
1.有网络的情况下,如何做到只有配置了缓存的接口才会缓存数据,并且每次请求都会走服务器接口呢?

在retrofit接口上我们配置了@Headers(“Cache-Control:public ,max-age=0”),添加了这个配置,在拦截器中就会拦截到cacheControl,通过cacheControl判空就能知道哪个接口是进行了缓存配置的,max-age=0,意思就是我们需要将数据缓存到本地,只是因为设置了有效期为0,就可以做到每次请求都会去访问服务器,而不是读取缓存。
没有配置过@Headers的接口,那么responseInterceptor拦截到的cacheControl是空的,因为我们设置cacheControl = “no-store”,这样就不会往本地缓存数据了。

2.无网络情况下配置过的接口是如何取缓存数据的而不去访问服务器,没有配置过的接口为何不会取缓存而是去访问服务器呢?

requestInterceptor拦截到的请求里根据cacheControl是否为空就可以得知哪个接口是配置过的,如果配置过了并且是无网络情况下,那么就设置only-if-cached来强制使用缓存,哪怕抛异常了也不会去访问服务器。而没有配置过的我们直接return了,走的还是默认的配置。

3.为何我在retrofit里设置了max-age=0,也就说缓存有效期是0,无网络条件下获取缓存数据时应该数据是过期的没法用才对啊,因为设置了0就意味着直接就是失效的啊

这就是关于max-age和max-stale的用法了,其实这两个用法都是一样的,在有网络的情况下max-age=0是有效的,所以有网的情况下肯定是判断了缓存无效而每次去访问服务器。然而在无网络的情况下我们在requestInterceptor设置了max-stale,也就是max-age和max-stale同时存在的情况下,我们取最大值,因为max-stale我们设置了30,那么缓存有效期就是30s了,那么再无网络情况下的30s内,缓存数据都是有效的。

猜你喜欢

转载自blog.csdn.net/qq_36356379/article/details/109804330