preface

I looked for some articles and found that they were not very clear. The setup was always a bit of a problem
This configuration, everyone’s needs are not the same, the implementation is certainly not the same.
Tell me about my needs:
The specified interface generates cache files, and other interfaces do not. 3. The specified interface uses cached data when the network is off. Other interfaces do not use cached dataCopy the code

1. Network interceptor (key)

Tip: Only Get requests can be cached

 Interceptor cacheInterceptor = new Interceptor() {@override public Response Intercept (Chain Chain) throws IOException {Request Request = chain.request(); String cacheControl = request.cachecontrol ().toString(); Use the network cache only if there is no network and the @headers annotation is added.if(! Utils.isOpenInternet()&&! Textutils.isempty (cacheControl)){// Reset the request body; Request = request.newBuilder() // Force the cache.cachecontrol (cachecontrol.force_cache).build(); } // If no annotations are added, no caching is doneif (TextUtils.isEmpty(cacheControl) || "no-store"Contains (cacheControl)) {// The response header is set to no cache cacheControl ="no-store";
                } else if(utils.isopenInternet ()) {// If there is a network, set the cache expiration time to 0 to obtain the latest data cacheControl ="public, max-age=" + 0;
                }else{/ /... } Response Response = chain.proceed(request);} Response = chain.proceed(request); HLog.i("httpInterceptor", cacheControl);
                return response.newBuilder()
                        .header("Cache-Control", cacheControl)
                        .removeHeader("Pragma")
                        .build();
        };
Copy the code

Add headers to the interface:

/** * only get requests can be cached. Lol except @headers (...) */ @headers ()"Cache-Control: public, max-age=" + 24 * 3600)
    @GET("url") Observable<? > queryInfo(@Query("userName") String userName);
Copy the code

Cache-control header parameters:

Public All content is cached (both client and proxy servers can cache it) Private content is cached only in the private cache (only clients can cache it, but proxy servers cannot cache it) no-cache No-cache is cached, but every time a response is sent to a client (browser), Caches evaluate the validity of cached responses to the server. No content in no-store will not be cached in cache or temporary Internet files max-age= XXX (XXX is numeric) The cached content will expire after XXX seconds. This option is only available in HTTP 1.1, If it is used with last-Modified, it can only be set in the request header, which is the same as that of max-age. If the value is set to max-stale and max-age at the same time, the cache expiration time is regarded as the longest. (Don't worry about that.)Copy the code

There are two more parameters:

Cachecontrol. FORCE_CACHE Forces the cache to be used. If no data is cached, 504(only-if-cached) is thrown.

These two Settings do not determine whether there is a network. You need to write your own judgment. Error Settings will result in data not being refreshed, or in the case of network access, data cannot be requested. You can switch according to your own needs.

2. Set the OkHttpClient

OkHttpClient client = new okHttpClient.builder () // AddlogInterceptor, printlogAddNetworkInterceptor (cacheInterceptor) addNetworkInterceptor(loggingInterceptor) addNetworkInterceptor(loggingInterceptor) AddInterceptor (cacheInterceptor) // Set the cache directory and the maximum size of the cache(10M. Cache (new)) Cache(MyApplication.getContext().getCacheDir(), 10240 * 1024)) .build();Copy the code

3. Complete code:

Public class RetrofitUtil {/** * server address */ private static final String API_HOST = constant.urls.baseurl; privateRetrofitUtil() {

    }

    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 = newInterceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); String cacheControl = request.cachecontrol ().toString(); // Cache is used only when there is no network and annotations are added.if(! Utils.isOpenInternet()&&! Textutils.isempty (cacheControl)){// Reset the request body; request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); } // If no annotations are added, no caching is doneif (TextUtils.isEmpty(cacheControl) || "no-store"Contains (cacheControl)) {// The response header is set to no cache cacheControl ="no-store";
                } else if(utils.isopenInternet ()) {// If there is a network, set the expiration event of the cache to 0 to obtain the latest data cacheControl ="public, max-age=" + 0;
                }else{/ /... } Response Response = chain.proceed(request);} 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();returnnew Retrofit.Builder() .client(client) .baseUrl(API_HOST) .addConverterFactory(FastjsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } private static class Instanace { private static final Retrofit retrofit = getInstanace(); }}Copy the code

The enclosed 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 (ifPresent). * <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 { voidlog(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); }}; } publicHttpLoggingInterceptor() {
        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) {// If MultipartBody, yeslogA lot of garbled stuff}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");
        returncontentEncoding ! = null && ! contentEncoding.equalsIgnoreCase("identity");
    }

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

Your love and reply is my biggest motivation – –