美文网首页
OKHTTP 缓存解析

OKHTTP 缓存解析

作者: heiheiwanne | 来源:发表于2017-04-10 11:41 被阅读207次

参考:OkHttp3 Cache

拆轮子系列:拆 OkHttp

  • 首先理解下缓存的几种cachecontrol ,这里网上很多,不多赘述

Cache-Control:

Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令有下几种:

  • Public指示响应可被任何缓存区缓存。
  • Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
  • no-cache指示请求或响应消息不能缓存
  • no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
  • max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
  • min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
  • max-stale指示客户机可以接收超出超时期间的响应消息。如果指定* * max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

缓存在okhttp中的处理有两种

一.官方推荐使用在request中添加CacheControl

  • CacheControl.FORCE_CACHE
  • CacheControl.FORCE_NETWORK
  • 3.使用maxStale
Request request = new Request.Builder()
                .cacheControl(new CacheControl.Builder()
                        .maxStale(10, TimeUnit.SECONDS)
                        .build())
                .url("http://publicobject.com/helloworld.txt")
                .build();
  • 4.maxAge
Request request = new Request.Builder()
               .cacheControl(new CacheControl.Builder()
                       .maxAge(10, TimeUnit.SECONDS)
                       .build())
               .url("http://publicobject.com/helloworld.txt")
               .build();

上边试过3、4 是在请求头中加入参数,然后在CacheInterceptor中进行了处理(借助CacheStrategy 类的private CacheStrategy getCandidate()方法).

/** Returns a strategy to use assuming the request can use the network. */
    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.
      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.
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl requestCaching = request.cacheControl();
      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);
    }

二.使用拦截器,Interceptor

OKHTTP之责任链理解

因为责任链的顺序是 cache -> your Application Interceptor -> network Interceptor 所以你的interceptor 要是缓存处理掉话需要 okhttpclient.addInterceptor(); 要是返回结果添加请求头的话需要使用addNetworkInterceptor();

public class OkLibCacheInterceptor implements Interceptor {

    private Context context;

    public OkLibCacheInterceptor(Context context) {
        this.context = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
        //判断网络状态  直接取缓存,是在最上层进行拦截,但是我们的OkLibCacheInterceptor 是addNetworkInterceptor() ,在网络请求之后进行的
        //所以这里不会被递归调用到,一旦网络失败便不会进入此拦截器了 ,学习下,所以这里的使用缓存是不管用的得需要在请求时加入,或者使用addInterceptor 放在网络拦截器之前
        if (!isNetworkConnected(context)) {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
            Log.e("OkLibCacheInterceptor", "暂无网络");
        }

        Log.e("新请求", "=request==" + request.toString());
        Response response = chain.proceed(request);

        if (isNetworkConnected(context)) {
            int maxAge = 60 * 60 * 24; // 有网络的时候从缓存1天后失效
            response = response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .build();
        } else {
            int maxStale = 60 * 60 * 24 * 28; // // 无网络缓存保存四周
            response = response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }
        Log.e("oklibCacheInter", response.headers().toMultimap().toString());
        return response;
    }

    public boolean isNetworkConnected(Context context) {
        if (context != null) {
            ConnectivityManager mConnectivityManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
            if (mNetworkInfo != null) {
                return mNetworkInfo.isAvailable();
            }
        }
        return false;
    }


}

NOTE

  • 1.默认okhttp缓存只支持get请求,不支持post请求,post需要自己进行缓存
  • 2.进行了抓包在使用maxAge的时候依然发起请求,当然我在使用拦截器的时候没事.这里我看了下官网的介绍是
This technique works even better in situations where a stale response is better than no response. To permit stale cached responses, use the max-stale directive with the maximum staleness in seconds:

Request request = new Request.Builder()
    .cacheControl(new CacheControl.Builder()
        .maxStale(365, TimeUnit.DAYS)
        .build())
    .url("http://publicobject.com/helloworld.txt")
    .build();

所以使用maxStale 进行处理

//todo 这里maxAge maxStale 区别还是没怎么搞懂,记录下

相关文章

网友评论

      本文标题:OKHTTP 缓存解析

      本文链接:https://www.haomeiwen.com/subject/gursattx.html