Okhttp緩存源碼分析和自定義緩存實(shí)現(xiàn)

緩存的一般思路

下面是我理解的網(wǎng)絡(luò)請(qǐng)求框架的緩存基本實(shí)現(xiàn)。大致的過(guò)程是有緩存用緩存的數(shù)據(jù),沒(méi)緩存發(fā)起http請(qǐng)求取數(shù)據(jù),得到最新數(shù)據(jù)后存到緩存里。


image.png

那么Okhttp怎么實(shí)現(xiàn)緩存的,我們從Okhttp發(fā)起一次請(qǐng)求的全過(guò)程中來(lái)看緩存是怎么實(shí)現(xiàn)的

Okhttp請(qǐng)求過(guò)程源碼分析

最簡(jiǎn)單的使用(以下代碼都是okhttp3.8.0為基礎(chǔ)):

Response response = client.newCall(request).execute()

追蹤到Call接口的實(shí)現(xiàn)類(lèi)RealCall的方法execute

@Override public Response execute() throws IOException {
  synchronized (this) {
    // 判斷是否在執(zhí)行,是則拋出異常
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  // 初始化跟蹤stack trace的對(duì)象,用來(lái)做日志,所以可以忽略先
  captureCallStackTrace();
  try {
    //將異步的請(qǐng)求丟到異步的雙端隊(duì)列(Deque<RealCall> runningSyncCalls)中等待處理,這里可以先忽略,直接看同步的結(jié)果
    client.dispatcher().executed(this);
    //獲取Response
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}

很明顯,獲取response在getResponseWithInterceptorChain這個(gè)方法里。這里代碼很簡(jiǎn)單,就是初始化一個(gè)interceptor列表,然后調(diào)用RealInterceptorChainproceed函數(shù)。

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

interceptors(攔截器)

那么這些interceptor有什么用呢,我們挑幾個(gè)重要的一一看一下,記住這些interceptors的add順序很重要
client.interceptors():
依次追蹤到interceptors的賦值的地方

    public Builder addInterceptor(Interceptor interceptor) {
      interceptors.add(interceptor);
      return this;
    }

    public List<Interceptor> interceptors() {
      return interceptors;
    }

這個(gè)是不是很熟悉,這個(gè)就是我們利用OkHttpClient.Builder builder構(gòu)造okhttpClient的地方傳入的interceptor,也就是常說(shuō)的application interceptor
CacheInterceptor:看名字很明顯是用來(lái)做緩存的
ConnectInterceptor:用來(lái)建立http連接
client.networkInterceptors():同client.interceptors(),是我們創(chuàng)建okhttpclient時(shí)傳入的networkInterceptor
CallServerInterceptor:向server發(fā)請(qǐng)求的
RealInterceptorChain.proceed(request)
追蹤到下面的方法,這時(shí)候傳入的streamAllocation,httpCodec,connection都是null,index=0

      public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
          RealConnection connection) throws IOException {
        // 判斷index是否越界
        if (index >= interceptors.size()) throw new AssertionError();
    
        calls++;
    
        // If we already have a stream, confirm that the incoming request will use it.
        if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must retain the same host and port");
        }
    
        // If we already have a stream, confirm that this is the only call to chain.proceed().
        if (this.httpCodec != null && calls > 1) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must call proceed() exactly once");
        }
    
        // Call the next interceptor in the chain.
        // 創(chuàng)建一個(gè)新的RealInterceptorChain,除了index+1,其他的參數(shù)都和上一個(gè)RealInterceptorChain保持不變
        RealInterceptorChain next = new RealInterceptorChain(
            interceptors, streamAllocation, httpCodec, connection, index + 1, request);
        // 獲取當(dāng)前index的Interceptor
        Interceptor interceptor = interceptors.get(index);
        // 執(zhí)行當(dāng)前Interceptor的intercept方法,傳入的參數(shù)為下一個(gè)RealInterceptorChain
        Response response = interceptor.intercept(next);
    
        // Confirm that the next interceptor made its required call to chain.proceed().
        if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
          throw new IllegalStateException("network interceptor " + interceptor
              + " must call proceed() exactly once");
        }
    
        // Confirm that the intercepted response isn't null.
        if (response == null) {
          throw new NullPointerException("interceptor " + interceptor + " returned null");
        }
    
        return response;
      }

所以RealInterceptorChain.proceed的大致過(guò)程如下

  1. 獲取下一個(gè)RealInterceptorChain next
  2. 調(diào)用當(dāng)前的interceptor的intercept方法,傳入?yún)?shù)為next

所以我們要追蹤interceptor的intercept方法,下面我以我項(xiàng)目里的一個(gè)做統(tǒng)計(jì)的intercptor為例來(lái)分析

public class NetStatisticsInterceptor implements Interceptor {
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl httpUrl = request.url();
        HttpUrl.Builder urlBuilder = httpUrl.newBuilder();
        if (httpUrl.queryParameter("app_version") == null) {
            urlBuilder.addQueryParameter("app_version", BaseConfig.versionName);
            }
            // 用chain.request()構(gòu)造一個(gè)新的傳入統(tǒng)計(jì)參數(shù)的request,作為參數(shù)調(diào)用chain.proceed
            return chain.proceed(request.newBuilder().url(urlBuilder.build()).build());
        }
    }

這里對(duì)舊的oldRequest做了一堆處理,加入了一些通用的統(tǒng)計(jì)參數(shù),包裝成生成了一個(gè)新的newRequest,然后調(diào)用chain.proceed方法,這里又會(huì)重新調(diào)用RealInterceptorChain.proceed的方法,只是參數(shù)index+1了,request為重新包裝后的request了(其他的參數(shù)也可能變了,取決于Interceptor怎么寫(xiě))。接著又會(huì)走到RealInterceptorChain.proceed代碼里,走下一個(gè)Interceptor的流程。
可以得出如下結(jié)論:

  1. 只要Interceptor的intercept方法調(diào)用了chain.proceed(request),就會(huì)調(diào)用Interceptor列表里的下一個(gè)Interceptor;反之可以不調(diào)用chain.proceed來(lái)打斷這個(gè)請(qǐng)求鏈
  2. 我們自定義的application interceptor和network interceptor時(shí),都必須返回chain.proceed得到的結(jié)果;否則就會(huì)打斷okhttp內(nèi)部的請(qǐng)求鏈
  3. 寫(xiě)application interceptor時(shí),在調(diào)用chain.proceed(request)之前包裝request
  4. 寫(xiě)network interceptor時(shí),在調(diào)用chain.proceed(request)之后得到的response包裝response

看到這里的代碼設(shè)計(jì),是不是和職責(zé)鏈模式很相似,唯一不同的是okhttp利用index自增的方式來(lái)實(shí)現(xiàn)每個(gè)攔截器的傳遞。這里我必須感嘆下,代碼設(shè)計(jì)的真的很巧妙,還有就是設(shè)計(jì)模式這東西平常看不出有啥用,到實(shí)際碰到了真的很棒。

了解完這些攔截器怎么運(yùn)行的,接下來(lái)具體看看各個(gè)攔截器是怎么把請(qǐng)求給串聯(lián)起來(lái)的。

應(yīng)用攔截器(client.interceptors())

這個(gè)我們常說(shuō)的application interceptor因?yàn)樵跀r截器list的最前面,所以最先執(zhí)行,一般用于給request做一些簡(jiǎn)單的包裝,例如添加參數(shù),修改header等

CacheInterceptor

直接看intercept方法

      @Override public Response intercept(Chain chain) throws IOException {
        // 根據(jù)url獲取本地緩存
        Response cacheCandidate = cache != null
            ? cache.get(chain.request())
            : null;
    
        long now = System.currentTimeMillis();
    
        // 用當(dāng)前時(shí)間now、當(dāng)前的請(qǐng)求request、本地緩存cacheCandidate來(lái)構(gòu)造CacheStrategy對(duì)象
        // 調(diào)用strategy對(duì)象的get方法去判斷本地緩存cacheCandidate是否可用
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;
    
        if (cache != null) {
          cache.trackResponse(strategy);
        }
    
        if (cacheCandidate != null && cacheResponse == null) {
          closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
        }
    
        // If we're forbidden from using the network and the cache is insufficient, fail.
        if (networkRequest == null && cacheResponse == null) {
          return new Response.Builder()
              .request(chain.request())
              .protocol(Protocol.HTTP_1_1)
              .code(504)
              .message("Unsatisfiable Request (only-if-cached)")
              .body(Util.EMPTY_RESPONSE)
              .sentRequestAtMillis(-1L)
              .receivedResponseAtMillis(System.currentTimeMillis())
              .build();
        }
    
        // If we don't need the network, we're done.如果networkRequest為null就表示走本地緩存
        if (networkRequest == null) {
          return cacheResponse.newBuilder()
              .cacheResponse(stripBody(cacheResponse))
              .build();
        }
    
        // 走后面的interceptor鏈去取網(wǎng)絡(luò)數(shù)據(jù)得到networkResponse
        Response networkResponse = null;
        try {
          networkResponse = chain.proceed(networkRequest);
        } finally {
          // If we're crashing on I/O or otherwise, don't leak the cache body.
          if (networkResponse == null && cacheCandidate != null) {
            closeQuietly(cacheCandidate.body());
          }
        }
    
        // If we have a cache response too, then we're doing a conditional get.
        if (cacheResponse != null) {
          if (networkResponse.code() == HTTP_NOT_MODIFIED) {
            Response response = cacheResponse.newBuilder()
                .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();
            networkResponse.body().close();
    
            // Update the cache after combining headers but before stripping the
            // Content-Encoding header (as performed by initContentStream()).
            cache.trackConditionalCacheHit();
            cache.update(cacheResponse, response);
            return response;
          } else {
            closeQuietly(cacheResponse.body());
          }
        }
    
        // 用networkResponse、cacheResponse構(gòu)造新的response
        Response response = networkResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
    
        if (cache != null) {
          if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
            // 如果response符合緩存的策略需要緩存,則put到cache中
            // Offer this request to the cache.
            // 這里追蹤到put中,可以發(fā)現(xiàn)只有method為GET才會(huì)add到cache中,所以okhttp是只支持get請(qǐng)求的緩存的;且key為response.request().url()
            CacheRequest cacheRequest = cache.put(response);
            return cacheWritingResponse(cacheRequest, response);
          }
    
          if (HttpMethod.invalidatesCache(networkRequest.method())) {
            try {
              cache.remove(networkRequest);
            } catch (IOException ignored) {
              // The cache cannot be written.
            }
          }
        }
    
        return response;
      }

大致流程如下

  1. 獲取本地緩存cacheCandidate
  2. 如果本地緩存可用則直接返回cacheCandidate,從而打斷interceptor鏈
  3. 走剩下的interceptor獲取networkResponse
  4. networkResponse、cacheResponse構(gòu)造新的response
  5. 根據(jù)新的response里的header定制緩存策略,存入緩存中


    image.png
CacheStrategy

從上面的代碼來(lái)看,主要的緩存策略都是在這個(gè)類(lèi)里實(shí)現(xiàn)。
我們關(guān)注這兩個(gè)變量,networkRequest為null就不走網(wǎng)絡(luò)取數(shù)據(jù),cacheResponse為null則不用緩存

      /** The request to send on the network, or null if this call doesn't use the network. */
      public final @Nullable Request networkRequest;
    
      /** The cached response to return or validate; or null if this call doesn't use a cache. */
      public final @Nullable Response cacheResponse;

        public CacheStrategy get() {
          CacheStrategy candidate = getCandidate();
    
          if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
            // We're forbidden from using the network and the cache is insufficient.
            return new CacheStrategy(null, null);
          }
    
          return candidate;
        }

追蹤到public CacheStrategy get()方法

        private CacheStrategy getCandidate() {
          // No cached response.
          if (cacheResponse == null) {
            return new CacheStrategy(request, null);
          }
    
          // Drop the cached response if it's missing a required handshake.
          // 請(qǐng)求為https且緩存沒(méi)有TLS握手
          if (request.isHttps() && cacheResponse.handshake() == null) {
            return new CacheStrategy(request, null);
          }
    
          // If this response shouldn't have been stored, it should never be used
          // as a response source. This check should be redundant as long as the
          // persistence store is well-behaved and the rules are constant.
          // 跟進(jìn)緩存Response的code,response和request的cache-control的noStore字段判斷是否需要緩存
          if (!isCacheable(cacheResponse, request)) {
            return new CacheStrategy(request, null);
          }
    
          CacheControl requestCaching = request.cacheControl();
          // 請(qǐng)求的header不要緩存
          if (requestCaching.noCache() || hasConditions(request)) {
            return new CacheStrategy(request, null);
          }
    
          long ageMillis = cacheResponseAge();
          long freshMillis = computeFreshnessLifetime();
    
          if (requestCaching.maxAgeSeconds() != -1) {
            freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
          }
    
          long minFreshMillis = 0;
          if (requestCaching.minFreshSeconds() != -1) {
            minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
          }
    
          long maxStaleMillis = 0;
          CacheControl responseCaching = cacheResponse.cacheControl();
          if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
            maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
          }
    
          if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
            Response.Builder builder = cacheResponse.newBuilder();
            if (ageMillis + minFreshMillis >= freshMillis) {
              builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
            }
            long oneDayMillis = 24 * 60 * 60 * 1000L;
            if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
              builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
            }
            return new CacheStrategy(null, builder.build());
          }
    
          // Find a condition to add to the request. If the condition is satisfied, the response body
          // will not be transmitted.
          String conditionName;
          String conditionValue;
          if (etag != null) {
            conditionName = "If-None-Match";
            conditionValue = etag;
          } else if (lastModified != null) {
            conditionName = "If-Modified-Since";
            conditionValue = lastModifiedString;
          } else if (servedDate != null) {
            conditionName = "If-Modified-Since";
            conditionValue = servedDateString;
          } else {
            return new CacheStrategy(request, null); // No condition! Make a regular request.
          }
    
          Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
          Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
    
          Request conditionalRequest = request.newBuilder()
              .headers(conditionalRequestHeaders.build())
              .build();
          return new CacheStrategy(conditionalRequest, cacheResponse);
        }

看這個(gè)方法大部分都是返回CacheStrategy(request, null)也就是走網(wǎng)絡(luò),那么我們直接看唯一的返回緩存的代碼:return new CacheStrategy(null, builder.build());什么條件呢?

    // ageMillis是response的maxAge時(shí)間和當(dāng)前時(shí)間算出來(lái)的cache的有效時(shí)間。。。。具體我也沒(méi)看明白哈
    //response不是no-cache且(ageMillis+request的min-fresh時(shí)間)<(request的max-age時(shí)間+request的max-stale)
    !responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis

總結(jié)下

  1. request的header有only-if-cached:啥緩存都不用
  2. 沒(méi)有緩存:當(dāng)然不用緩存
  3. request為https且緩存丟失了TLS握手:不用緩存
  4. request或者response的header有no-store:不用緩存
  5. response除了200一些的status code以外:不用緩存
  6. 滿足這個(gè)條件ageMillis + minFreshMillis < freshMillis + maxStaleMillis的request和response:用緩存
  7. 其他的一些情況(我看暈了)
  8. 總之就是根據(jù)request和response的header的cache-control來(lái)做緩存,我們可以嚴(yán)格按照http協(xié)議的來(lái)做緩存策略,而不用去看okhttp協(xié)議怎么實(shí)現(xiàn)的(嗯,okhttp應(yīng)該是嚴(yán)格按照http協(xié)議來(lái)寫(xiě)的吧?)

ConnectInterceptor,CallServerInterceptor

ConnectInterceptor

Opens a connection to the target server and proceeds to the next interceptor
關(guān)鍵類(lèi):StreamAllocation

CallServerInterceptor

This is the last interceptor in the chain. It makes a network call to the server.

最佳實(shí)踐

服務(wù)端控制緩存

  1. 客戶端請(qǐng)求時(shí),header傳入想要的緩存時(shí)間策略,例如
 @Headers("Cache-Control: no-cache")// 不要緩存
 @Headers("Cache-Control: public, max-age=604800")//緩存時(shí)間為604800秒
  1. 服務(wù)端指定緩存策略,返回相應(yīng)的response Cache-Control

然而很不幸,大部分的服務(wù)端都沒(méi)有返回Cache-Control來(lái)控制緩存,所以就有了下面的辦法

客戶端控制緩存時(shí)間

  1. 客戶端傳入header
    @Headers("Cache-Control: public, max-age=30")//緩存時(shí)間為30秒
    
  2. 添加networkInterceptor
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response originalResponse = chain.proceed(request);
        if (TextUtils.isEmpty(originalResponse.header("Cache-Control"))) {
            // 這里把request傳入的header傳遞給response
            return originalResponse.newBuilder().header("Cache-Control", request.header("Cache-Control")).build();
        }
        return originalResponse;
     }
    

客戶端控制緩存時(shí)間,同時(shí)要求無(wú)網(wǎng)絡(luò)的時(shí)候使用緩存

有網(wǎng)絡(luò)的時(shí)候同上;無(wú)網(wǎng)絡(luò)的時(shí)候,如果超過(guò)一天則顯示error,沒(méi)超過(guò)一天用緩存

  1. 客戶端傳入header同上
  2. networkInterceptor同上
  3. 添加applicationInterceptor,傳入一個(gè)max-age為無(wú)限大數(shù)的header就能強(qiáng)制用緩存了,或者設(shè)置Cache-Control的FORCE_CACHE
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        CacheControl cacheControl = request.cacheControl();
        boolean noCache = cacheControl.noCache() || cacheControl.noStore() || cacheControl.maxAgeSeconds() == 0;
        // 如果header強(qiáng)制要求不用緩存就不走這個(gè)邏輯
        if (!noCache && !NetworkUtils.isNetworkAvailable(context)) {
            Request.Builder builder = request.newBuilder();
            //if network not available, load in cache
            CacheControl newCacheControl = new CacheControl.Builder()
                   .maxAge(Integer.MAX_VALUE, TimeUnit.SECONDS).build();
            request = builder.cacheControl(newCacheControl).build();
            return chain.proceed(request);
         }
         return chain.proceed(request);
    }
    

客戶端控制緩存時(shí)間,同時(shí)要求無(wú)網(wǎng)絡(luò)的時(shí)候使用緩存,并且這個(gè)緩存超過(guò)一天就失效了

基本同上,但是傳入的header不是maxAge而是max-stale,設(shè)置緩存過(guò)期后還能可用的時(shí)間為一天即可

CacheControl newCacheControl = new CacheControl.Builder().maxStale(ONE_DAY, TimeUnit.SECONDS).build();

客戶端控制緩存時(shí)間,request的時(shí)間和response的時(shí)間不同

前面的幾個(gè)方式,response的header實(shí)際上是從request取出來(lái)的,也就是說(shuō)我們的response的header時(shí)間和request的header時(shí)間是一樣的。但是如果要不一樣的情況怎么辦呢?舉個(gè)我項(xiàng)目里的例子

  1. 發(fā)送A請(qǐng)求(緩存為30分鐘),發(fā)現(xiàn)這個(gè)商品沒(méi)有買(mǎi)
  2. 花錢(qián)把這個(gè)商品買(mǎi)了,再次請(qǐng)求A請(qǐng)求刷新頁(yè)面
  3. 因?yàn)锳請(qǐng)求有30分鐘緩存沒(méi)有刷新數(shù)據(jù);于是乎我修改了request的header為不使用緩存(也就是age為0),這時(shí)數(shù)據(jù)刷新了
  4. 幾分鐘后,我下次進(jìn)來(lái)這個(gè)頁(yè)面,再次請(qǐng)求A(因?yàn)橹癮ge為0,所以并沒(méi)有緩存),我又發(fā)了次請(qǐng)求(實(shí)際我期望的是使用緩存的)

實(shí)際上我希望的是在步驟3里發(fā)送A請(qǐng)求時(shí),request的header為age=0,response的age=30min,那么怎么實(shí)現(xiàn)呢,所以提供了下面的方法

首先提供了一個(gè)工具類(lèi),用來(lái)存放header的時(shí)間和生成header。這里用ThreadLocal變量存放了response的時(shí)間

    public final class NetAccessStrategy {
        private NetAccessStrategy() {
    
        }
        private static final ThreadLocal<Integer> localCacheTime = new ThreadLocal<>();
    
        public static void setThreadLocalCacheTime(int cacheTime) {
            localCacheTime.set(cacheTime);
        }
    
        public static int getThreadLocalCacheTime() {
            Integer time = localCacheTime.get();
            localCacheTime.remove();
            if (time == null) {
                return 0;
            }
            return time;
        }
    
        public static final String NET_REQUEST = "net-";
    
    
        /**
         * @param requestCacheTime 本地緩存在超過(guò)這個(gè)時(shí)間后失效
         * @param localCacheTime   本地緩存的時(shí)間
         * @return
         */
        public static String getRequestNetHeader(int requestCacheTime, int localCacheTime) {
            return NET_REQUEST + requestCacheTime + "-" + +localCacheTime;
        }
    
        public static int[] getRequestCacheTime(String netHeader) {
            int index1 = netHeader.indexOf("-", 1);
            int index2 = netHeader.indexOf("-", index1 + 1);
            int time1 = -1;
            int time2 = -1;
            if (index1 != -1 && index2 != -1) {
                try {
                    time1 = Integer.parseInt(netHeader.substring(index1 + 1, index2));
                } catch (NumberFormatException ignored) {
                }
                try {
                    time2 = Integer.parseInt(netHeader.substring(index2 + 1));
                } catch (NumberFormatException ignored) {
                }
            }
            return new int[]{time1, time2};
        }
    }

在application Interceptor里加上

    // 如果發(fā)現(xiàn)net-開(kāi)頭的自定義header時(shí)
    if (header.startsWith(NetAccessStrategy.NET_REQUEST)) {
                Request.Builder builder = request.newBuilder();
                // 解析得到request和response的時(shí)間
                int[] timeArray = NetAccessStrategy.getRequestCacheTime(header);
                // 傳入request的age時(shí)間
                CacheControl cacheControl = new CacheControl.Builder().maxAge(timeArray[0], TimeUnit.SECONDS).build();
                // 存入response的時(shí)間
                NetAccessStrategy.setThreadLocalCacheTime(timeArray[1]);
                builder.cacheControl(cacheControl);
                return chain.proceed(builder.build());
            }

在network Interceptor里加上

     Response originalResponse = chain.proceed(request);
                int time = NetAccessStrategy.getThreadLocalCacheTime();
                if (time > 0) {
                    // 取出response time,如果大于0則放到header里
                    return originalResponse.newBuilder().header("Cache-Control", "public, max-age=" + time)
                            .build();
                }
                return originalResponse.newBuilder().header("Cache-Control", request.header("Cache-Control"))
                        .build();

發(fā)請(qǐng)求時(shí)加上header,這樣就能實(shí)現(xiàn)強(qiáng)制刷新,且緩存為300秒的功能了

NetAccessStrategy.getRequestNetHeader(0, 300)

我的主頁(yè)

PS:https://github.com/nppp1990,平常隨便寫(xiě)寫(xiě),還有kotlin的demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容