美文网首页
OkHttp学习笔记(三)-----缓存策略

OkHttp学习笔记(三)-----缓存策略

作者: 带带太师兄 | 来源:发表于2018-04-23 11:19 被阅读0次

    一般http/https的缓存策略都是,如果某一时间段内第二次请求服务器,服务器的数据和上次相比较并没有做任何改变,那么就会返回一个code 304,表示:client可以使用缓存,然后client从缓存中取出response返回给用户。

    Okhttp的缓存方式是将可复用的responsebody通过CacheInterceptor缓存到文件中,先来看CacheInterceptor这个类

    在CacheInterceptor这个类中只有一个属性

    InternalCache cache 根据官方文档的描述他是一个okhttp内部缓存接口,大家应该使用Cache类而不是这个内部使用的类。它提供了一堆get put remove方法如下:

    1. Responseget(Request request)throws IOException;     //通过request获得response,不出意外,应该可以通过这个方法获得缓存中的response

    2. CacheRequestput(Responseresponse)throws IOException; //将request存入缓存中

    3. void remove(Request request)throws IOException;  //移除request

    4. void update(Responsecached, Responsenetwork); //更新已经缓存的response

    5. void trackConditionalCacheHit();   //如果本次请求的response是从缓存中拿的,那么就埋下本次命中缓存策略成功的信息,具体的在具体行为中实现

    6. void trackResponse(CacheStrategy cacheStrategy); //同样是埋点,跟踪这个响应,但是这里需要传CacheStrategy,它是一个缓存策略,也就是说,这个方法将会跟踪,使用传入缓存策略缓存的response。CacheStrategy,用于本次决定本次请求是使用网络还是缓存,如果指定固定的请求策略,比如使用缓存,当缓存过期,或者服务端数据更新了,这个缓存策略会让请求变得复杂。

    这样 内部缓存接口都分析完了,它只是提供request,response的获取或跟踪或移除

    接下去,我们继续分析这个cacheintercepetor,okhttp的intercepetor就是拦截器,先拦截下来,然后转发出去

    来看一下cacheintercepetor在哪里用到,点击之后跳到了RealCall中

    没有错,就是RealCall里面的getResponseWithInterceptorChain()这个方法,首先经过各种拦截器,然后返回response。在interceptors.add(new CacheInterceptor(client.internalCache()));//添加缓存拦截器的时候,或传入通过OkhttpClient构建的自定义拦截器,如果没有传入自定义拦截器,那就会传入系okhttp系统自带的默认拦截器,当添加完之后又初始化了一个RealInterceptorChain对象,很明显,这是一个拦截器责任链,将会链式调用各个拦截器的方法,这里不做深究。

    继续往下看Cacheintercepetor到底做了什么,开始真正分析了!!

    首先是最重要的intercept()方法

    public Response intercept(Chain chain)throws IOException;在拦截之后,会放回response对象,

    接下去看intercept的第一步
    Response cacheCandidate =cache !=null ?cache.get(chain.request()):null;如果传入的Cache不为空的话,就通过cache拿response,但是这个response并不是最后转发给client的,因为这里并不知道服务器有没有修改数据。因此这里是一个候选response

    第二步,获得当前时间,用于接下去查看请缓存信息是否过期
    long now = System.currentTimeMillis(); //获得系统时间

    第三步获得缓存策略,根据缓存策略构造request和response

        CacheStrategy strategy =new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;
        if (cache !=null) {
        cache.trackResponse(strategy);
        }    

    在这里我们又用到了这个缓存策略,那么我们来看一下这个缓存策略到底是干什么的,首先他是通过工厂模式构造的,
    先看一下它的构造方法
    public Factory(long nowMillis, Request request, Response cacheResponse),它需要传入一个nowMillis,一个request,一个response,而这个response我们可以从命名中发现,它是从缓存中取出的response,那么传入这三个参数是用来干什么呢?

    首先在构造方法中,直接使用了传进来的cacheResponse

    通过response拿到了缓存时发送请求的时间,接收到相应的时间,以及响应头并且从响应头中取出各个参数

    ServerDate:服务器的时间
    Expires:应该在什么时候认为文档已经过期,从而不再缓存它?
    LastModified:服务器最后一次修改的时间
    ETag:也是用于判断服务器资源是否有修改
    Age:从原始服务器到代理缓存形成的估算时间

    获得了这些头信息之后,就要通过这些头信息来确定是否需要使用缓存

    CacheStrategy.Factory(now, chain.request(), cacheCandidate).get() 
    其中的.get()是返回一个CacheStrategy,构造CacheStrategy对象的则是getCandidate()这个方法点进去之后可以看到该方法会判断各种条件来返回一个CacheStrateg

    例如:if (cacheResponse ==null) {return new CacheStrategy(request, null);}  //如果没有缓存的response,则构造一个新网络的请求

    所以这个CacheStrategy是通过响应头信息与服务器端信息进行对比,最后返回是否使用新的网络请求或者直接使用缓存

    然后我们回到interce(),获得了缓存策略之后,接下去就是拿到重新构造的request和response

    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;

    接着通过拿到的request和response来判断返回一个真正的response给用户
    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();
    }

    如果不使用新的网络请求,并且缓存的Response已经过期,或者没有缓存的response,那么就会返回一个504错误

    经过上述判断之后,如果不使用新的网络请求,并且cacheResponse可用,那么就利用cacheResponse返回一个新的response

    if (networkRequest ==null) {
    return cacheResponse.newBuilder()
    .cacheResponse(stripBody(cacheResponse))
    .build();
    }

    第三种情况:如果即使用新的request,并且缓存可用,那就通过新的response更新响应头

    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();
        cache.trackConditionalCacheHit();
        cache.update(cacheResponse, response);
        return response;
      }else {
    closeQuietly(cacheResponse.body());
      }
    }

    就这样通过CacheIntercepetor 我们得到了最后可以使用的response

    相关文章

      网友评论

          本文标题:OkHttp学习笔记(三)-----缓存策略

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