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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • 2.okhttp3.0整体流程:1).创建okhttpclient客户端对象,表示所有的http请求的客户端的类,...
    无为3阅读 379评论 0 1
  • 缓存分类 http请求有服务端和客户端之分。因此缓存也可以分为两个类型服务端侧和客户端侧。 缓存——服务端常见的服...
    安仔夏天勤奋阅读 599评论 1 0
  • 本文就是讲解在OKHTTP中如何配置缓存。 HTTP协议中缓存相关 为了更好的讲解OKHTTP怎么设置缓存,我们追...
    TendaZhang阅读 3,034评论 7 19
  • 有点三九天的感觉了,今天天气很冷但是难得没有雾霾,昨晚的那阵大风算是起作用啦。好天气里,心情自然也好,连和同...
    火色石榴花阅读 108评论 0 0