利用OkHttp缓存机制实现无网缓存

OkHttp实现缓存

标题字越少,内容含金量越高。

需求描述

需求

在有网的情况下,正常进行网络请求,然后把响应缓存到本地;在无网的情况下,从本地拿到缓存,返回给调用方。

限制

不能改变服务器的API,服务器的API没有Cache-Control字段。

思路

利用OkHttp的拦截器实现。

OkHttp请求过程:OkHttp的缓存机制(CacheInterceptor)会自动判断我们提交的Request中的Cache-Control头:如果是only-if-cache(FORCE_CACHE),则只能从缓存中获取,不能进行网络请求,如果获取缓存失败,则返回一个504的错误响应码;如果是no-cache则只从网络中获取。

OkHttp响应过程:当正常的网络请求返回之后,CacheInterceptor会自动判断Response的Cache-Control头,如果是only-if-cache,则会缓存到本地;如果是no-cache,则不缓存。

所以,我们可以在响应返回到CacheInterceptor之前拦截Response,强制加上Cache-Control: only-if-cache,保存缓存;然后在请求发出到CacheInterceptor之前拦截Resquest,判断当前网络状态,如果无网,则强制加上Cache-Control: only-if-cache,让请求从缓存中直接获取。

这里写图片描述

这里写图片描述

扫描二维码关注公众号,回复: 2806614 查看本文章

关于拦截器的具体源码分析可以看我的另一篇博客:OkHttp源码解析

实现

拦截器

我们先来看看拦截器的责任链工作模式:
这里写图片描述

我们提交的请求首先会经过我们自定义的Interceptors,然后经过缓存(CacheInterceptor)处理,接着经过也是我们自定义的NetworkInterceptors,最后才交给CallServerInterceptor传输到TCP流。

如何实现一个拦截器?

//示例
static class DemoInterceptor implements Interceptor{
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request req = chain.request(); //获得请求
            Response res = chain.process(req); //交给责任链下一环执行,最后回传一个响应
            return res; //返回响应给上一层
        }
    }

编写请求拦截器

先写一个请求拦截器,在请求发出去之前检查网络,如果无网,则要求使用缓存:

public class Util {
    //判断网络连接状态
    public static boolean isNetworkConnected() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        return networkInfo != null && networkInfo.isAvailable();
    }
}
static class RequestCacheInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request.Builder builder = chain.request().newBuilder(); //在原来的request的基础上修改
            if (! Util.isNetworkConnected()) {
                //无网下强制缓存
                builder.cacheControl(CacheControl.FORCE_CACHE); //等同于添加only-if-cache
            }
            Request newRequest = builder.build();
            return chain.proceed(newRequest);
        }
    }

编写响应拦截器

再写一个响应拦截器,强制要求缓存:

    static class ResponseCacheInterceptor implements Interceptor{
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request()).newBuilder()
                    .removeHeader("Pragma") //移除影响
                    .removeHeader("Cache-Control") //移除影响
                    .addHeader("Cache-Control", CacheControl.FORCE_CACHE.toString()).build();
            return response;
        }
    }

添加到OkHttpClient

最后添加这些拦截器到OkHttpClient上:

OkHttpClient client = new OkHttpClient.Builder()
    .cache(new Cache(Util.getContext().getExternalCacheDir(), 50 * 8 * 1024 * 1024)) //设置缓存目录大小50MB
    .addInterceptor(new RequestCacheInterceptor()) //在用户端添加请求拦截器
    .addNetworkInterceptor(new ResponseCacheInterceptor()) //在网络端添加响应拦截器(注意和用户端的区别)
    .build();

效果图

在说一点就是,OkHttp只缓存GET请求,而且缓存是以URL作为键值(key)存储在本地,如果url改变(包括url参数改变),都会重新发起网络请求而不会使用缓存。

最最后献上效果图:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/mingC0758/article/details/81678959
今日推荐