Okhttp cache policy acquisition

Okhttp cache policy acquisition

  • In the cache interceptor, we mentioned that the second step is to obtain a cache policy object CacheStrategy

What is CacheStrategy and what is its use

  • Direct code

    • /**
       * Given a request and a cached response, this determines whether to use network, cache, or both.
       */
      public final class CacheStrategy {
        /** Send the request over the network. If the Call does not use the network request, null indicates that the network request is used*/
        public final @Nullable Request networkRequest;
      
        /**Cached response If Call Not applicable to cache. The value is null* Represents the use of caching/
        public final @Nullable Response cacheResponse;
      }
      
    • summary

      +What is it
      +It is a class that internally maintains a Request and a Response
      +What's the use
      +By giving the internal request and response values, you can determine whether the request uses network or cache, or both
      +Here, both are used to compare the network and cache. If the values are the same, the cache response is returned. If the values are different, the network response is returned

How are networkRequest and response values generated

  • To understand how to generate, we must first know where and how to obtain the CacheStrategy. Obviously, it is obtained in the CacheInterceptor

    • CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
      
    • You can see through cachestrategy Let's look at the code of Factory under the get method of Factory

      • public static class Factory {
           final long nowMillis;
           final Request request;
           final Response cacheResponse;
          
           /** Indicates when the server responded back to the client (if known) */
           private Date servedDate;
           private String servedDateString;
          
           /** The last modification date of the cached response, if known.
            * lastModified(Included in response) and if modified since (included in request)
            * If the request is sent successfully for the first time, the server will return lastModified and if
            *  When the request is sent again, the client will send an if modified since at the same time, and its value is the value of lastModified
            *  After receiving the request, the server will take out the value for comparison. If it is the same, the server resources have not changed and the cache can be used
            *  */
           private Date lastModified;
           private String lastModifiedString;
          
           /**
            * The expiration date of the cached response, if known.
            * If both this field and max age are set, max age is preferred.
            */
           private Date expires;
          
           /**
            * The extension header set by OkHttp specifies the timestamp when the cached HTTP request is first initiated.
            */
           private long sentRequestMillis;
          
           /**
            * The extension header set by OkHttp specifies the timestamp when the cached HTTP response is received for the first time.
            */
           private long receivedResponseMillis;
          
           /**
            * A string of values calculated by the server is used to compare whether data changes occur in the same request server
            * Label of the cached response. Generally, it works with if non match
            * When the request is sent successfully for the first time, the server will return an etag
            * When the request is sent again, the client will send an if none match at the same time, and its value is the value of Etag
            * The server compares the values after receiving them
            * If it is the same, the code of the response is http_ NOT_ The modified (304) data is not changed and is directly fetched from the cache
            */
           private String etag;
          
           /** Age Indicates the time after the intermediate link (CDN or cache server) gets data from the server.
            * If the client sees 0 seconds, it means the latest data obtained from the server.. */
           private int ageSeconds = -1;
           /**Factory class to get policy network cache or both*
            * @param nowMillis current time 
            * @param request Current request
            * @param cacheResponse Cache response
            */
           public Factory(long nowMillis, Request request, Response cacheResponse) {
             this.nowMillis = nowMillis;
             this.request = request;
             this.cacheResponse = cacheResponse;
             // If the cached response is not empty, the send timestamp and response timestamp are updated
             if (cacheResponse != null) {
               this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
               this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
               Headers headers = cacheResponse.headers();
               // Traverse the header update service response time, cache response expiration time, last response time, Etag tag, and the time after getting the data from the server
               for (int i = 0, size = headers.size(); i < size; i++) {
                 String fieldName = headers.name(i);
                 String value = headers.value(i);
                 if ("Date".equalsIgnoreCase(fieldName)) {
                   servedDate = HttpDate.parse(value);
                   servedDateString = value;
                 } else if ("Expires".equalsIgnoreCase(fieldName)) {
                   expires = HttpDate.parse(value);
                 } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
                   lastModified = HttpDate.parse(value);
                   lastModifiedString = value;
                 } else if ("ETag".equalsIgnoreCase(fieldName)) {
                   etag = value;
                 } else if ("Age".equalsIgnoreCase(fieldName)) {
                   ageSeconds = HttpHeaders.parseSeconds(value, -1);
                 }
               }
             }
           }
          
           /**
            * Returns the satisfied policy network cache or both
            */
           public CacheStrategy get() {
             CacheStrategy candidate = getCandidate();
          	 // The networkRequest object is not empty and the use of the network is prohibited
            if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
              // Returns a cache policy object that prohibits the use of the network and cannot use the cache
              return new CacheStrategy(null, null);
            }
          
            return candidate;
          }
          /**
           * Returns true if the request contains a condition that prevents the server from sending a client local response.
           * When a request is queued with its own conditions, the built-in response cache will not be used.
           * If-Modified-Since The last response time value is Modified
           * If-None-Match  The value is the value of the Etag tag
           *    + These two values indicate that the version specified from the request header if modified since or if none match has not been modified.
           *    + This means that there is no need to retransmit data, and the client still has previously cached data
           *
           */
          private static boolean hasConditions(Request request) {
            return request.header("If-Modified-Since") != null || request.header("If-None-Match") != null;
          }
        }
        
      • You can see that the specific generation rules are in the getCandidate() method

        •     /** Returns the policy to use, assuming that the request can use the network. */
              private CacheStrategy getCandidate() {
                // There are no cached responses. The policy containing only network requests is returned directly
                if (cacheResponse == null) {
                  return new CacheStrategy(request, null);
                }
          
                // If it is https, judge whether the response handshakes
                // If the cache request lacks handshake information, the policy containing only the network request is returned directly
                if (request.isHttps() && cacheResponse.handshake() == null) {
                  return new CacheStrategy(request, null);
                }
          
                //  Judge whether the cache response can be used for another request (such as whether the status code is correct). If it cannot be used, directly return the policy containing only network requests
                if (!isCacheable(cacheResponse, request)) {
                  return new CacheStrategy(request, null);
                }
          
                CacheControl requestCaching = request.cacheControl();
                // Determine whether the cache controller is nocache
                // Or the request header contains if modified since (last response time) and if none match (value of Etag)
                // If these two values exist, the server needs to verify whether the local cache can still be used, so the policy containing only network requests is returned;
                if (requestCaching.noCache() || hasConditions(request)) {
                  return new CacheStrategy(request, null);
                }
          
                CacheControl responseCaching = cacheResponse.cacheControl();
                //Omit some codes
                /**
                 * If the cache controller is not nocache and agemillis + minfreshmillis < freshmillis + maxstalemillis
                 *    The cache is not expired and available, but a warning of 110 or 113 will be added to the response header)
                 *    Returns a policy that contains only the cache
                 */
                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());
                }
          
                // Cache expired, unavailable
                // If there is no condition if none match or if modified since, a policy containing only network requests is returned
                // If the condition if none match or if modified since exists, add it to the request header
                // Returns a policy that contains the network request and uses the cache
                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.
                }
          	 // Build a network request and set the header 
                Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
                Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);
          
                Request conditionalRequest = request.newBuilder()
                    .headers(conditionalRequestHeaders.build())
                    .build();
                  // Returns a policy that contains both network requests and caches
                return new CacheStrategy(conditionalRequest, cacheResponse);
              }
          

Generate rule summary

  • Note here
    • Returns a policy that contains only network requests, indicating that only network requests are used
    • Returns a cache only policy that uses only cached data
    • Returns a policy that contains both network requests and caches, indicating that both are used
      • The server determines whether the cache expires
      • If it does not expire, use the cache and update the cache
      • If it expires, use the network and write it to the cache. This part is in the network interceptor
  • Rule summary
  • Use network request policy
    • 1. If there is no cached response. The policy containing only network requests is returned directly
    • 2. If it is an https request and the handshake information is missing, the policy containing only the network request is returned directly
    • 3. Judge whether the cache response can be used for another request (such as whether the status code is correct). If it cannot be used, directly return the policy containing only network requests
    • 4. Judge whether the cache controller is nocache, or the request header contains if modified since, and if none match returns a policy containing only network requests
    • 5. If the cache is expired, unavailable, and there is no condition if none match or if modified since, a policy containing only network requests is returned
  • Use cache policy
    • 1. If it is judged that the cache controller is not nocache and the cache is not expired and available, the policy containing only the cache is returned
  • Use both
    • 1. If the cache expires and if none match or if modified since exists, a request is constructed and added to the header of requests. At this time, the policy containing both network requests and cache is returned
      • At this time, 304 judgment is required in the cache interceptor to judge whether the data has been updated

effect

  • Determines whether the current request uses network, cache, or both.

Keywords: Java OkHttp

Added by gilbertwang on Tue, 04 Jan 2022 01:35:18 +0200