zoukankan      html  css  js  c++  java
  • okhttp3.4.1+retrofit2.1.0实现离线缓存

    关于Retrofit+OkHttp的强大这里就不多说了,还没了解的同学可以自行去百度。这篇文章主要讲如何利用Retrofit+OkHttp来实现一个较为简单的缓存策略:
    即有网环境下我们请求数据时,如果没有缓存或者缓存过期了,就去服务器拿数据,并且将新缓存保存下来,如果有缓存而且没有过期,则直接使用缓存。无网环境下我们请求数据时,缓存没过期则直接使用缓存,缓存过期了则无法使用,需要重新联网获取服务器数据。

    缓存处理还是很有必要的,它有效的减少服务器负荷,降低延迟提升用户体验,同时也方便用户即使在没网络的情况下也能使用APP。

    之前一直有一个疑惑,既然Retrofit已经是对OkHttp的一个封装了,为什么还一直说Retrofit+OkHttp要一起搭配使用,后来才知道其实OKHttp很重要的一个作用,就是对一些网络请求的配置,例如连接超时,读取超时,以及一些缓存配置等。

    一、添加依赖
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

    二、配置OkHttpClient(设置缓存路径和缓存文件大小) 

    File httpCacheDirectory = new File(Environment.getExternalStorageDirectory(), "HttpCache");//这里为了方便直接把文件放在了SD卡根目录的HttpCache中,一般放在context.getCacheDir()中
    int cacheSize = 10 * 1024 * 1024;//设置缓存文件大小为10M
    Cache cache = new Cache(httpCacheDirectory, cacheSize);
    httpClient = new OkHttpClient.Builder()
                 .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时
                 .readTimeout(10, TimeUnit.SECONDS)//读取超时
                 .writeTimeout(10, TimeUnit.SECONDS)//写入超时
                 .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加自定义缓存拦截器(后面讲解),注意这里需要使用.addNetworkInterceptor
                 .cache(cache)//把缓存添加进来
                 .build();

    三、配置Retrofit

    retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(httpClient)//把OkHttpClient添加进来
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

    四、编写拦截器

      我们知道其实Retrofit+OkHttp的缓存主要通过拦截器实现,所以主要做的功夫也在拦截器里面。

     static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
    
                Request request = chain.request();
                //网上很多示例代码都对在request请求前对其进行无网的判断,其实无需判断,无网自动访问缓存
    //            if(!NetworkUtil.getInstance().isConnected()){
    //                request = request.newBuilder()
    //                        .cacheControl(CacheControl.FORCE_CACHE)//只访问缓存
    //                        .build();
    //            }
                Response response = chain.proceed(request);
    
                if (NetworkUtil.getInstance().isConnected()) {
                    int maxAge = 60;//缓存失效时间,单位为秒
                    return response.newBuilder()
                            .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                            .header("Cache-Control", "public ,max-age=" + maxAge)
                            .build();
                } else {
                    //这段代码设置无效
    //                int maxStale = 60 * 60 * 24 * 28; // 无网络时,设置超时为4周
    //                return response.newBuilder()
    //                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
    //                        .removeHeader("Pragma")
    //                        .build();
                }
                return response;
            }
        };

    到这里,其实已经可以实现了我们开头所说的缓存效果了。

      但是,上面设置的每个接口缓存时间都一样,例如我现在想让不同接口的缓存数据失效时间都不一样,甚至有些接口不缓存数据,应该怎么做呢?其实也很简单

    首先我们只需要在接口前面添加@Headers参数(max-age代表缓存时间,单位为秒,示例中表示缓存失效时间为60s,想要多少时间可以自行设置),不设置@Headers参数则不进行缓存。

        @Headers("Cache-Control:public ,max-age=60")
        @GET("getBusiness.action")//商店信息
        Call<RestaurantInfoModel> getRestaurantInfo(@Query("userId") String userId,@Query("businessId") String businessId);

     同时,我们的缓存拦截器也要做下简单的修改(去掉了之前的注释代码)

        static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
    
                Request request = chain.request();
                Response response = chain.proceed(request);
    
                if (NetworkUtil.getInstance().isConnected()) {
                    //获取头部信息
                    String cacheControl =request.cacheControl().toString();
                    return response.newBuilder()
                            .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                            .header("Cache-Control", cacheControl)
                            .build();
                }
                return response;
            }
        };

    *注意:

    1.只能缓存Get请求的接口,不能缓存Post请求的接口

    2.OkHttpClient需要用.addNetworkInterceptor添加缓存拦截器,不能使用.addInterceptor,也无需两者同时使用。

    3.此方法无需服务器端任何操作,适用于服务器端没有其他缓存策略,如果服务器端有自己的缓存策略代码应该做相应的修改,以适应服务器端。

    附上所有代码:

    /**
     * 简单封装的Retroit初始化类
     */
    public class initRetrofit {
        private static String baseUrl = "http://202.171.212.154:8080/hh/";
        private static OkHttpClient httpClient;
        private static Retrofit retrofit;
    
        public static Retrofit initRetrofit() {
            //缓存路径和大小
            File httpCacheDirectory = new File(Environment.getExternalStorageDirectory(), "HttpCache");
            int cacheSize = 10 * 1024 * 1024;
            Cache cache = new Cache(httpCacheDirectory, cacheSize);
    
            //日志拦截器
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    
            httpClient = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)//设置连接超时
                    .readTimeout(10, TimeUnit.SECONDS)//读取超时
                    .writeTimeout(10, TimeUnit.SECONDS)//写入超时
                    .addInterceptor(interceptor)//添加日志拦截器
                    .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加缓存拦截器
                    .cache(cache)//把缓存添加进来
                    .build();
    
            retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(httpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            return retrofit;
        }
    
        public static RetrofitAPI getService() {
            return initRetrofit().create(RetrofitAPI.class);
        }
    
    //    //缓存拦截器,不同接口不同缓存
    //    static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
    //        @Override
    //        public Response intercept(Chain chain) throws IOException {
    //
    //            Request request = chain.request();
    //            Response response = chain.proceed(request);
    //
    //            if (NetworkUtil.getInstance().isConnected()) {
    //                String cacheControl =request.cacheControl().toString();
    //                return response.newBuilder()
    //                        .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
    //                        .header("Cache-Control", cacheControl)
    //                        .build();
    //            }
    //            return response;
    //        }
    //    };
    
        //缓存拦截器,统一缓存60s
        static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
    
                Request request = chain.request();
                Response response = chain.proceed(request);
    
                if (NetworkUtil.getInstance().isConnected()) {
                    int maxAge = 60*60*24*2;//缓存失效时间,单位为秒
                    return response.newBuilder()
                            .removeHeader("Pragma")//清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                            .header("Cache-Control", "public ,max-age=" + maxAge)
                            .build();
                }
                return response;
            }
        };
    }
  • 相关阅读:
    预处理与编译阶段
    联合体
    linux shell
    二维数组、字符数组、指针数组涉及字符串和具体元素问题
    二级指针的简单运用
    halcon算子翻译——get_image_type
    halcon算子翻译——get_image_time
    halcon算子翻译——get_image_size
    Halcon算子翻译——get_image_pointer3
    Halcon算子翻译——get_image_pointer1_rect
  • 原文地址:https://www.cnblogs.com/cxk1995/p/5996586.html
Copyright © 2011-2022 走看看