【缓存策略】Retrofit+OkHttp实现缓存处理

来源:互联网 发布:淘宝上有铁匠铺吗 编辑:程序博客网 时间:2024/05/18 20:10

早先对于服务器数据缓存处理一般是本地SP或者Sqlite;现在网络请求改为Retrofit+OkHttp,OkHttp是有缓存策略的,

今天我们就来说怎么实现Retrofit与OkHttp的缓存实现。


使用缓存的目的

减少服务器负荷,降低延迟提升用户体验。复杂的缓存策略会根据用户当前的网络情况采取不同的缓存策略,比如在2g网络很差的情况下,提高缓存使用的时间;不用的应用、业务需求、接口所需要的缓存策略也会不一样,需要数据的实时性,采用缓存就是无意义!根据实际应用情况,制定自己的缓存策略。

Retrofit+OkHttp的缓存机制

在响应请求之后在 data/data/<包名>/cache 下建立一个response 文件夹,保持缓存数据。
这样我们就可以在请求的时候,如果判断到没有网络,自动读取缓存的数据。
同样这也可以实现,在我们没有网络的情况下,重新打开App可以浏览的之前显示过的内容。
也就是:判断网络,有网络,则从网络获取,并保存到缓存中,无网络,则从缓存中获取。


实现缓存

1、开启缓存
这一步是设置缓存路径,以及缓存大小
<span style="font-size:14px;">File httpCacheDirectory = new File(context.getExternalCacheDir(), "responses");//设置缓存 10MCache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);client = new OkHttpClient.Builder().cache(cache).build();</span>
2、设置 OkHttp 拦截器
主要是拦截操作,包括控制缓存的最大生命值,控制缓存的过期时间

两个操作都是在 Interceptor 中进行的

  • 通过 CacheControl 控制缓存数据

 CacheControl.Builder cacheBuilder = new CacheControl.Builder(); cacheBuilder.maxAge(0, TimeUnit.SECONDS);//多次访问一个接口,<span style="white-space:pre"></span>设置请求缓存时间,超过时间重新请求,否则去缓存 cacheBuilder.maxStale(365,TimeUnit.DAYS);//这个是控制缓存的过时时间 CacheControl cacheControl = cacheBuilder.build();

  • 设置拦截器
Request request = chain.request();if(!StateUtils.isNetworkAvailable(MyApp.mContext)){ request = request.newBuilder()         .cacheControl(cacheControl)         .build();}Response originalResponse = chain.proceed(request);if (StateUtils.isNetworkAvailable(MyApp.mContext)) { int maxAge = 60; // read from cache return originalResponse.newBuilder()         .removeHeader("Pragma")         .header("Cache-Control", "public ,max-age=" + maxAge)         .build();} else { int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale return originalResponse.newBuilder()         .removeHeader("Pragma")         .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)         .build();}

.maxAge(0,TimeUnit.SECONDS)设置的时间比拦截器长是不起效果

设置比拦截器设置的时间短就会以这个时间为主,我觉得是为了方便控制。

maxStale(365, TimeUnit.DAYS)设置的是过时时间,okthhp缓存分成了两个来考虑,一个是为了请求时直接拿缓存省流量,一个是为了下次进入应用时可以直接拿缓存。

直接上代码

private HttpControl(final Context context) {        cookieStore = new PersistentCookieStore(context);        CookieHandler cookieHandler = new CookieManager(cookieStore,                CookiePolicy.ACCEPT_ALL);        Interceptor interceptor = new Interceptor() {            @Override            public Response intercept(Chain chain) throws IOException {                Request request = chain.request();                /**                 * 未联网获取缓存数据                 */                if (!CheckHasNet.isNetWorkOk(context)) {                    //在20秒缓存有效,此处测试用,实际根据需求设置具体缓存有效时间                    CacheControl cacheControl = new CacheControl.Builder()                            .maxStale(30, TimeUnit.DAYS)                            .build();                    request = request.newBuilder()                            .cacheControl(cacheControl)                            .build();                }                return chain.proceed(request);            }        };        HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {            @Override            public void log(String message) {                Log.d("MyTAG", "OkHttp: " + message);            }        });        logging.setLevel(HttpLoggingInterceptor.Level.BODY);        File httpCacheDirectory = new File(context.getExternalCacheDir(), "responses");        //设置缓存 10M        Cache cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);        //1.创建Retrofit对象        client = new OkHttpClient.Builder().addInterceptor(logging)                .addInterceptor(interceptor)//离线                .addNetworkInterceptor(provideCacheInterceptor())//在线                .cache(cache)                .readTimeout(30000, TimeUnit.MILLISECONDS)                .connectTimeout(30000, TimeUnit.MILLISECONDS)                .cookieJar(new JavaNetCookieJar(cookieHandler))                .build();        retrofit = new Retrofit.Builder().client(client).baseUrl(Constant.BASEURL)// 定义访问的主机地址                .validateEagerly(true)//                .addConverterFactory(GsonConverterFactory.create())//解析方法 Gson                .addConverterFactory(JsonConverterFactory.create())//解析方法//                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                .build();    }    public static Interceptor provideCacheInterceptor ()    {        return new Interceptor()        {            @Override            public Response intercept (Chain chain) throws IOException            {                Response response = chain.proceed( chain.request() );                // re-write response header to force use of cache                // 正常访问同一请求接口(多次访问同一接口),给30秒缓存,超过时间重新发送请求,否则取缓存数据                CacheControl cacheControl = new CacheControl.Builder()                        .maxAge(3, TimeUnit.SECONDS )                        .build();                return response.newBuilder()                        .header("Cache-Control", cacheControl.toString() )                        .build();            }        };    }    public PersistentCookieStore getCookieStore() {        return cookieStore;    }    /**     * 单例模式     *     * @return     */    public static HttpControl getInstance(Context context) {        if (instance == null) {            synchronized (HttpControl.class) {                if (instance == null) {                    instance = new HttpControl(context);                }            }        }        return instance;    }    public OkHttpClient getClient() {        return client;    }    public void setClient(OkHttpClient client) {        this.client = client;    }    public <T> T create(final Class<T> service) {        return retrofit.create(service);    }


注意

1、缓存是在每一次网络请求之后,重新保存的,所以在超过缓存过期时间后,Retrofit会在检查到没缓存之后自动请求网络服务器数据

2、缓存数据也是需要网络下载的,所以在网络不好的情况下,可能不能立即缓存

3、根据自身实际情况,制定缓存策略



0 0
原创粉丝点击