美文网首页
Android 笔记: OkhttpInterceptor Ca

Android 笔记: OkhttpInterceptor Ca

作者: silencefun | 来源:发表于2020-03-06 21:41 被阅读0次

    Anroid OKhttp笔记1 流程分析
    Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
    Android OkhttpInterceptor 笔记:BridgeInterceptor
    Android OkhttpInterceptor 笔记:ConnectInterceptor
    Android OkhttpInterceptor 笔记:CacheInterceptor
    Android OkhttpInterceptor 笔记:CallServerInterceptor
    Android Okhttp笔记:ConnectionPool
    Android Okhttp3:Dispatcher分析笔记


    一、流程代码逻辑分析

    CacheInterceptor是okhttp中缓存拦截器,是负责http请求的缓存处理 ,流程:
    1.读取缓存
    2.创建缓存策略,强制缓存、对比缓存等,
    3.根据策略,不使用网络,又没有缓存的直接报错,并返回错误码504。
    4.根据策略,不使用网络,有缓存的直接返回。
    5.前面两个都没有返回,继续执行下一个Interceptor,即ConnectInterceptor。
    6.接收到网络结果,如果响应code式304,则使用缓存,返回缓存结果。
    7.读取网络结果。
    8.对数据进行缓存。
    9.返回网络读取的结果。

    其中header 强制缓存使用的的两个标识:
    Expires:Expires的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。到期时间是服务端生成的,客户端和服务端的时间可能有误差。
    Cache-Control:Expires有个时间校验的问题,所以HTTP1.1采用Cache-Control替代Expires。

    Cache-Control的取值:

    private::客户端可以缓存。
    public::客户端和代理服务器都可缓存。
    max-age=xxx: 缓存的内容将在 xxx 秒后失效
    no-cache::需要使用对比缓存来验证缓存数据。
    no-store:所有内容都不会缓存;强制缓存,对比缓存都不会触发。

    1.流程分析

    当从上个拦截器中获取到http请求时,会从缓存里面取出对应的响应(之前缓存过的),如果没有,返回null。然后会根据request和获取到的缓存的response生成一个缓存策略CacheStrategy。

    //如果配置了缓存:优先从缓存中读取Response
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
    long now = System.currentTimeMillis();
    //缓存策略,该策略通过某种规则来判断缓存是否有效
     CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    

    缓存策略器是使用缓存还是请求网络获取新的数据。内部有两个属性:networkRequest和cacheResponse,在 CacheStrategy 内部会对这个两个属性在特定的情况赋值。

    networkRequest:若是不为 null ,表示需要进行网络请求
    cacheResponse:若是不为 null ,表示可以使用本地缓存

    //如果根据缓存策略strategy禁止使用网络,并且缓存无效,直接返回空的Response
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          。。。
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)//空的body
          。。。
          .build();
    }
    
    //如果根据缓存策略strategy禁止使用网络,且有缓存则直接使用缓存
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
    
    //需要网络
    Response networkResponse = null;
    try {//执行下一个拦截器,发起网路请求
      networkResponse = chain.proceed(networkRequest);
    } finally {
      。。。
    }
    
    //本地有缓存,
    if (cacheResponse != null) {
      //并且服务器返回304状态码(说明缓存还没过期或服务器资源没修改)
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        //使用缓存数据
        Response response = cacheResponse.newBuilder()
            。。。
            .build();
          。。。。
         //返回缓存 
        return response;
      } else {
        closeQuietly(cacheResponse.body());
      }
    }
    
    //如果网络资源已经修改:使用网络响应返回的最新数据
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    
    //将最新的数据缓存起来
    if (cache != null) {
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
    
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }
    //返回最新的数据
    return response;
    

    2.CacheStrategy

    策略器,负责判断是使用缓存还是请求网络获取新的数据。

    获取方法

    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    

    其中 cacheCandidate它表示的是从缓存中取出的 Response 对象,有可能为null(在缓存为空的时候),在 new CacheStrategy.Factory 内部如果 cacheCandidate 对象不为 null ,那么会取出 cacheCandidate 的头信息,并且将其保存到 CacheStrategy 属性中。

    get()

    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;
    }
    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();
     //....
    }
    

    代码显示的几种情况都是要发起请求的,包括:
    没有对应的缓存结果;
    https请求却没有握手信息;
    不允许缓存的请求(包括一些特殊状态码以及Header中明确禁止缓存)。

    3.实现缓存

     // 存入到缓存中去
        CacheRequest cacheRequest = cache.put(response); 
    
    
      @Nullable CacheRequest put(Response response) {
    String requestMethod = response.request().method();
    if (HttpMethod.invalidatesCache(response.request().method())) {
      try {
        remove(response.request());
      } catch (IOException ignored) {
        // The cache cannot be written.
      }
      return null;
    }
    if (!requestMethod.equals("GET")) {
      // Don't cache non-GET responses. We're technically allowed to cache
      // HEAD requests and some POST requests, but the complexity of doing
      // so is high and the benefit is low.
      return null;
    }
    if (HttpHeaders.hasVaryAll(response)) {
      return null;
    }
    Entry entry = new Entry(response);
    DiskLruCache.Editor editor = null;
    try {
      editor = cache.edit(key(response.request().url()));
      if (editor == null) {
        return null;
      }
      entry.writeTo(editor);
      return new CacheRequestImpl(editor);
    } catch (IOException e) {
      abortQuietly(editor);
      return null;
    }
     }
    

    其中 invalidatesCache()

       public static boolean invalidatesCache(String method) {
    return method.equals("POST")
        || method.equals("PATCH")
        || method.equals("PUT")
        || method.equals("DELETE")
        || method.equals("MOVE");     // WebDAV
    

    }
    对方法是POST,PATCH,PUT,DELETE,MOVE的请求,将缓存清除掉,这些是不应该被缓存的。然后再明确确认GET方法才会被缓存。
    然后由 Cache.Entry 构造的entry writeTo把editor 写入,其实就是将请求信息按顺序写入到DiskLruCache中,最终由DiskLruCache写入到磁盘中。


    Anroid OKhttp笔记1 流程分析
    Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
    Android OkhttpInterceptor 笔记:BridgeInterceptor
    Android OkhttpInterceptor 笔记:ConnectInterceptor
    Android OkhttpInterceptor 笔记:CacheInterceptor
    Android OkhttpInterceptor 笔记:CallServerInterceptor
    Android Okhttp笔记:ConnectionPool
    Android Okhttp3:Dispatcher分析笔记

    相关文章

      网友评论

          本文标题:Android 笔记: OkhttpInterceptor Ca

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