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

一般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学习笔记(三)-----缓存策略)