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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/knockheart/article/details/52814022

早先对于服务器数据缓存处理一般是本地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");
//设置缓存 10M
Cache 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、根据自身实际情况,制定缓存策略



猜你喜欢

转载自blog.csdn.net/knockheart/article/details/52814022