okHttp--Retrofit網絡緩存設置總結

前言

找了些文章,發現說的都不是很清楚.設置始終有點問題
這個配置,每一個人的需求不同,實現狀況確定也不同.
說說個人需求:
1.有網的時候全部接口不使用緩存
2.指定的接口產生緩存文件,其餘接口不會產生緩存文件
3.無網的時候指定的接口使用緩存數據.其餘接口不使用緩存數據
複製代碼

1.網絡攔截器(關鍵)

提示:只能緩存Get請求

Interceptor cacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                //拿到請求體
                Request request = chain.request();

                //讀接口上的@Headers裏的註解配置
                String cacheControl = request.cacheControl().toString();

                //判斷沒有網絡而且添加了@Headers註解,才使用網絡緩存.
                if (!Utils.isOpenInternet()&&!TextUtils.isEmpty(cacheControl)){
                    //重置請求體;
                    request = request.newBuilder()
                              //強制使用緩存
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }

             //若是沒有添加註解,則不緩存
                if (TextUtils.isEmpty(cacheControl) || "no-store" .contains(cacheControl)) {
                    //響應頭設置成無緩存
                    cacheControl = "no-store";
                } else if (Utils.isOpenInternet()) {
                    //若是有網絡,則將緩存的過時時間,設置爲0,獲取最新數據
                    cacheControl = "public, max-age=" + 0;
                }else {
                    //...若是無網絡,則根據@headers註解的設置進行緩存.
                }
                Response response = chain.proceed(request);
                HLog.i("httpInterceptor", cacheControl);
                return response.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
        };
複製代碼

具體接口中的使用,添加headers:

/**
   * 只能緩存get請求.
   * 這裏我設置了1天的緩存時間
   * 接口隨便寫的.哈哈,除了 @Headers(...),其它代碼沒啥參考價值.
   */
    @Headers("Cache-Control: public, max-age=" + 24 * 3600)
    @GET("url")
    Observable<?> queryInfo(@Query("userName") String userName);
複製代碼

關於Cache-Control頭的參數說明:

public	全部內容都將被緩存(客戶端和代理服務器均可緩存)

private	內容只緩存到私有緩存中(僅客戶端能夠緩存,代理服務器不可緩存)

no-cache	no-cache是會被緩存的,只不過每次在向客戶端(瀏覽器)提供響應數據時,緩存都要向服務器評估緩存響應的有效性。 

no-store	全部內容都不會被緩存到緩存或 Internet 臨時文件中

max-age=xxx (xxx is numeric)	緩存的內容將在 xxx 秒後失效, 這個選項只在HTTP 1.1可用, 並若是和Last-Modified一塊兒使用時, 優先級較高

max-stale和max-age同樣,只能設置在請求頭裏面。

同時設置max-stale和max-age,緩存失效的時間按最長的算。(這個其實不用糾結)
複製代碼

還有2個參數:

CacheControl.FORCE_CACHE 強制使用緩存,若是沒有緩存數據,則拋出504(only-if-cached) CacheControl.FORCE_NETWORK 強制使用網絡,不使用任何緩存.json

這兩個設置,不會判斷是否有網.須要本身寫判斷. 設置錯誤會致使,數據不刷新,或者有網狀況下,請求不到數據 這兩個很關鍵..能夠根據本身的需求,進行切換.瀏覽器

2.設置OkHttpClient

OkHttpClient client = new OkHttpClient.Builder()
                //添加log攔截器,打印log信息,代碼後面貼出
                .addInterceptor(loggingInterceptor)
                //添加上面代碼的攔截器,設置緩存
                .addNetworkInterceptor(cacheInterceptor)
                //這個也要添加,不然無網的時候,緩存設置不會生效
                .addInterceptor(cacheInterceptor)
                //設置緩存目錄,以及最大緩存的大小,這裏是設置10M
                .cache(new Cache(MyApplication.getContext().getCacheDir(), 10240 * 1024))
                .build();

複製代碼

3.完整的代碼:

public class RetrofitUtil {
    /**
     * 服務器地址
     */
    private static final String API_HOST = Constant.URLS.BASEURL;
  private RetrofitUtil() {

    }

    public static Retrofit getRetrofit() {
        return Instanace.retrofit;
    }

    private static Retrofit getInstanace() {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                HLog.i("RxJava", message);
            }
        });
        Interceptor cacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                //有網的時候,讀接口上的@Headers裏的註解配置
                String cacheControl = request.cacheControl().toString();
                //沒有網絡而且添加了註解,才使用緩存.
                if (!Utils.isOpenInternet()&&!TextUtils.isEmpty(cacheControl)){
                    //重置請求體;
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }

             //若是沒有添加註解,則不緩存
                if (TextUtils.isEmpty(cacheControl) || "no-store" .contains(cacheControl)) {
                    //響應頭設置成無緩存
                    cacheControl = "no-store";
                } else if (Utils.isOpenInternet()) {
                    //若是有網絡,則將緩存的過時事件,設置爲0,獲取最新數據
                    cacheControl = "public, max-age=" + 0;
                }else {
                    //...若是無網絡,則根據@headers註解的設置進行緩存.
                }
                Response response = chain.proceed(request);
                HLog.i("httpInterceptor", cacheControl);
                return response.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
            }
        };
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(loggingInterceptor)
                .addNetworkInterceptor(cacheInterceptor)
                .addInterceptor(cacheInterceptor)
                .cache(new Cache(MyApplication.getContext().getCacheDir(), 10240 * 1024))
                .build();
        return new Retrofit.Builder()
                .client(client)
                .baseUrl(API_HOST)
                .addConverterFactory(FastjsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

    }

    private static class Instanace {
        private static final Retrofit retrofit = getInstanace();
    }
 }
複製代碼

附上HttpLoggingInterceptor

/**
 * Created by Sunflower on 2016/1/12.
 */
public class HttpLoggingInterceptor implements Interceptor {
    private static final Charset UTF8 = Charset.forName("UTF-8");

    public enum Level {
        /**
         * No logs.
         */
        NONE,
        /**
         * Logs request and response lines.
         * <p/>
         * Example:
         * <pre>{@code
         * --> POST /greeting HTTP/1.1 (3-byte body)
         * <p/>
         * <-- HTTP/1.1 200 OK (22ms, 6-byte body)
         * }</pre>
         */
        BASIC,
        /**
         * Logs request and response lines and their respective headers.
         * <p/>
         * Example:
         * <pre>{@code
         * --> POST /greeting HTTP/1.1
         * Host: example.com
         * Content-Type: plain/text
         * Content-Length: 3
         * --> END POST
         * <p/>
         * <-- HTTP/1.1 200 OK (22ms)
         * Content-Type: plain/text
         * Content-Length: 6
         * <-- END HTTP
         * }</pre>
         */
        HEADERS,
        /**
         * Logs request and response lines and their respective headers and bodies (if present).
         * <p/>
         * Example:
         * <pre>{@code
         * --> POST /greeting HTTP/1.1
         * Host: example.com
         * Content-Type: plain/text
         * Content-Length: 3
         * <p/>
         * Hi?
         * --> END GET
         * <p/>
         * <-- HTTP/1.1 200 OK (22ms)
         * Content-Type: plain/text
         * Content-Length: 6
         * <p/>
         * Hello!
         * <-- END HTTP
         * }</pre>
         */
        BODY
    }

    public interface Logger {
        void log(String message);

        /**
         * A {@link Logger} defaults output appropriate for the current platform.
         */
        Logger DEFAULT = new Logger() {
            @Override
            public void log(String message) {
                Platform.get().log(Platform.WARN,message,null);
            }
        };
    }

    public HttpLoggingInterceptor() {
        this(Logger.DEFAULT);
    }

    public HttpLoggingInterceptor(Logger logger) {
        this.logger = logger;
    }

    private final Logger logger;

    private volatile Level level = Level.BODY;

    /**
     * Change the level at which this interceptor logs.
     */
    public HttpLoggingInterceptor setLevel(Level level) {
        if (level == null) throw new NullPointerException("level == null. Use Level.NONE instead.");
        this.level = level;
        return this;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Level level = this.level;

        Request request = chain.request();
        if (level == Level.NONE) {
            return chain.proceed(request);
        }

        boolean logBody = level == Level.BODY;
        boolean logHeaders = logBody || level == Level.HEADERS;

        RequestBody requestBody = request.body();
        boolean hasRequestBody = requestBody != null;

        String requestStartMessage = request.method() + ' ' + request.url();
        if (!logHeaders && hasRequestBody) {
            requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
        }
        logger.log(requestStartMessage);

        if (logHeaders) {

            if (!logBody || !hasRequestBody) {
                logger.log("--> END " + request.method());
            } else if (bodyEncoded(request.headers())) {
                logger.log("--> END " + request.method() + " (encoded body omitted)");
            } else if (request.body() instanceof MultipartBody) {
                //若是是MultipartBody,會log出一大推亂碼的東東
            } else {
                Buffer buffer = new Buffer();
                requestBody.writeTo(buffer);

                Charset charset = UTF8;
                MediaType contentType = requestBody.contentType();
                if (contentType != null) {
                    contentType.charset(UTF8);
                }

                logger.log(buffer.readString(charset));

//                logger.log(request.method() + " (" + requestBody.contentLength() + "-byte body)");
            }
        }

        long startNs = System.nanoTime();
        Response response = chain.proceed(request);
        long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
        logger.log(response.code() + ' ' + response.message() + " (" + tookMs + "ms" + ')');

        return response;
    }

    private boolean bodyEncoded(Headers headers) {
        String contentEncoding = headers.get("Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
    }

    private static String protocol(Protocol protocol) {
        return protocol == Protocol.HTTP_1_0 ? "HTTP/1.0" : "HTTP/1.1";
    }
}

複製代碼

您的喜歡與回覆是我最大的動力-_-緩存

相關文章
相關標籤/搜索