前言
在Android開(kāi)發(fā)中我們經(jīng)常要進(jìn)行各種網(wǎng)絡(luò)訪問(wèn),比如查看各類新聞、查看各種圖片。但有一種情形就是我們每次重復(fù)發(fā)送的網(wǎng)絡(luò)請(qǐng)求其實(shí)返回的內(nèi)容都是一樣的。比如一個(gè)電影類APP,每一次向服務(wù)器申請(qǐng)某個(gè)電影的相關(guān)信息,如封面、簡(jiǎn)介、演員表等等,它們的信息都是一樣的。顯然,這樣有點(diǎn)浪費(fèi)資源,最主要的是這些重復(fù)的請(qǐng)求產(chǎn)生了沒(méi)有必要的流量。流量、流量、流量?。?!重要的事情說(shuō)三遍!剛開(kāi)始工作的我也不懂,后來(lái)才發(fā)現(xiàn),流量是要付費(fèi)的,而且超貴,公司那么小,一個(gè)月要支付寬帶運(yùn)營(yíng)商巨額的流量費(fèi)用。所以領(lǐng)導(dǎo)們都想方設(shè)法地要節(jié)省帶寬。
其實(shí)這在整個(gè)軟件開(kāi)發(fā)中隨時(shí)可見(jiàn),解決的方法就是把重復(fù)請(qǐng)求的數(shù)據(jù)緩存在本地,并設(shè)置超時(shí)時(shí)間,在規(guī)定時(shí)間內(nèi),客戶端不再向遠(yuǎn)程請(qǐng)求數(shù)據(jù),而是直接從本地緩存中取數(shù)據(jù)。這樣一來(lái)提高了響應(yīng)速度,二來(lái)節(jié)省了網(wǎng)絡(luò)帶寬(也就是節(jié)省了錢)。 本文就是講解在OKHTTP中如何配置緩存。
HTTP協(xié)議中緩存相關(guān)
為了更好的講解OKHTTP怎么設(shè)置緩存,我們追根溯源先從瀏覽器的緩存說(shuō)起,這樣后面的OKHTTP緩存內(nèi)容自然更加好理解。
我這部分內(nèi)容也是經(jīng)網(wǎng)絡(luò)上查閱,這一篇寫得很詳細(xì)瀏覽器 HTTP 協(xié)議緩存機(jī)制詳解。以下內(nèi)容基本出自于此文章。
緩存分類
http請(qǐng)求有服務(wù)端和客戶端之分。因此緩存也可以分為兩個(gè)類型服務(wù)端側(cè)和客戶端側(cè)。
服務(wù)端側(cè)緩存
常見(jiàn)的服務(wù)端有Ngix和Apache。服務(wù)端緩存又分為代理服務(wù)器緩存和反向代理服務(wù)器緩存。常見(jiàn)的CDN就是服務(wù)器緩存。這個(gè)好理解,當(dāng)瀏覽器重復(fù)訪問(wèn)一張圖片地址時(shí),CDN會(huì)判斷這個(gè)請(qǐng)求有沒(méi)有緩存,如果有的話就直接返回這個(gè)緩存的請(qǐng)求回復(fù),而不再需要讓請(qǐng)求到達(dá)真正的服務(wù)地址,這么做的目的是減輕服務(wù)端的運(yùn)算壓力。
客戶端側(cè)緩存
客戶端主要指瀏覽器(如IE、Chrome等),當(dāng)然包括我們的OKHTTPClient.客戶端第一次請(qǐng)求網(wǎng)絡(luò)時(shí),服務(wù)器返回回復(fù)信息。如果數(shù)據(jù)正常的話,客戶端緩存在本地的緩存目錄。當(dāng)客戶端再次訪問(wèn)同一個(gè)地址時(shí),客戶端會(huì)檢測(cè)本地有沒(méi)有緩存,如果有緩存的話,數(shù)據(jù)是有沒(méi)有過(guò)期,如果沒(méi)有過(guò)期的話則直接運(yùn)用緩存內(nèi)容。
而我們講的就是客戶端的緩存。
緩存中重要的概念
Cache-Control
Cache-Control是什么呢?先別急, 我們先用觀察一個(gè)結(jié)果,用Fiddler監(jiān)聽(tīng)瀏覽器訪問(wèn)http://blog.csdn.net/briblue。如下圖:
HTTP/1.1 200 OK
Server: openresty
Date: Mon, 24 Oct 2016 09:00:34 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
Vary: Accept-Encoding
Cache-Control: private
X-Powered-By: PHP 5.4.28
Content-Encoding: gzip
可以看到頭信息中有這么一行:
Cache-Control: private
Cache-control是由服務(wù)器返回的Response中添加的頭信息,它的目的是告訴客戶端是要從本地讀取緩存還是直接從服務(wù)器摘取消息。它有不同的值,每一個(gè)值有不同的作用。
max-age:這個(gè)參數(shù)告訴瀏覽器將頁(yè)面緩存多長(zhǎng)時(shí)間,超過(guò)這個(gè)時(shí)間后才再次向服務(wù)器發(fā)起請(qǐng)求檢查頁(yè)面是否有更新。對(duì)于靜態(tài)的頁(yè)面,比如圖片、CSS、Javascript,一般都不大變更,因此通常我們將存儲(chǔ)這些內(nèi)容的時(shí)間設(shè)置為較長(zhǎng)的時(shí)間,這樣瀏覽器會(huì)不會(huì)向?yàn)g覽器反復(fù)發(fā)起請(qǐng)求,也不會(huì)去檢查是否更新了。
s-maxage:這個(gè)參數(shù)告訴緩存服務(wù)器(proxy,如Squid)的緩存頁(yè)面的時(shí)間。如果不單獨(dú)指定,緩存服務(wù)器將使用max-age。對(duì)于動(dòng)態(tài)內(nèi)容(比如文檔的查看頁(yè)面),我們可告訴瀏覽器很快就過(guò)時(shí)了(max-age=0),并告訴緩存服務(wù)器(Squid)保留內(nèi)容一段時(shí)間(比如,s-maxage=7200)。一旦我們更新文檔,我們將告訴Squid清除老的緩存版本。
must-revalidate:這告訴瀏覽器,一旦緩存的內(nèi)容過(guò)期,一定要向服務(wù)器詢問(wèn)是否有新版本。
proxy-revalidate:proxy上的緩存一旦過(guò)期,一定要向服務(wù)器詢問(wèn)是否有新版本。
no-cache:不做緩存。
no-store:數(shù)據(jù)不在硬盤中臨時(shí)保存,這對(duì)需要保密的內(nèi)容比較重要。
public:告訴緩存服務(wù)器, 即便是對(duì)于不該緩存的內(nèi)容也緩存起來(lái),比如當(dāng)用戶已經(jīng)認(rèn)證的時(shí)候。所有的靜態(tài)內(nèi)容(圖片、Javascript、CSS等)應(yīng)該是public的。
private:告訴proxy不要緩存,但是瀏覽器可使用private cache進(jìn)行緩存。一般登錄后的個(gè)性化頁(yè)面是private的。
no-transform: 告訴proxy不進(jìn)行轉(zhuǎn)換,比如告訴手機(jī)瀏覽器不要下載某些圖片。
max-stale指示客戶機(jī)可以接收超出超時(shí)期間的響應(yīng)消息。如果指定max-stale消息的值,那么客戶機(jī)可以接收超出超時(shí)期指定值之內(nèi)的響應(yīng)消息。
我們上面的例子是Cache-Control:private。說(shuō)明服務(wù)器希望客戶端不要緩存消息,但是可以進(jìn)行private cache方法進(jìn)行緩存。這是因?yàn)?a target="_blank" rel="nofollow">http://blog.csdn.net/briblue是我的博客頁(yè)面,與用戶系統(tǒng)相關(guān),所以為了安全起見(jiàn),建議用private cache的方式緩存。
在OKHttp開(kāi)發(fā)中我們常見(jiàn)到的有下面幾個(gè):
- max-age
- no-cache
- max-stale
expires
expires的效果等同于Cache-Control,不過(guò)它是Http 1.0的內(nèi)容,它的作用是告訴瀏覽器緩存的過(guò)期時(shí)間,在此時(shí)間內(nèi)瀏覽器不需要直接訪問(wèn)服務(wù)器地址直接用緩存內(nèi)容就好了。 expires最大的問(wèn)題在于如果服務(wù)器時(shí)間和本地瀏覽器相差過(guò)大的問(wèn)題。那樣誤差就很大。所以基本上用Cache-Control:max-age=多少秒的形式代替。
Last-Modified/If-Modified-Since
這個(gè)需要配合Cache-Control使用
- Last-Modified:標(biāo)示這個(gè)響應(yīng)資源的最后修改時(shí)間。web服務(wù)器在響應(yīng)請(qǐng)求時(shí),告訴瀏覽器資源的最后修改時(shí)間。
- If-Modified-Since:當(dāng)資源過(guò)期時(shí)(使用Cache-Control標(biāo)識(shí)的max-age),發(fā)現(xiàn)資源具有Last-Modified聲明,則再次向web服務(wù)器請(qǐng)求時(shí)帶上頭 If-Modified-Since,表示請(qǐng)求時(shí)間。web服務(wù)器收到請(qǐng)求后發(fā)現(xiàn)有頭If-Modified-Since 則與被請(qǐng)求資源的最后修改時(shí)間進(jìn)行比對(duì)。若最后修改時(shí)間較新,說(shuō)明資源又被改動(dòng)過(guò),則響應(yīng)整片資源內(nèi)容(寫在響應(yīng)消息包體內(nèi)),HTTP 200;若最后修改時(shí)間較舊,說(shuō)明資源無(wú)新修改,則響應(yīng)HTTP 304 (無(wú)需包體,節(jié)省瀏覽),告知瀏覽器繼續(xù)使用所保存的cache。
Etag/If-None-Match
這個(gè)也需要配合Cache-Control使用
Etag對(duì)應(yīng)請(qǐng)求的資源在服務(wù)器中的唯一標(biāo)識(shí)(具體規(guī)則由服務(wù)器決定),比如一張圖片,它在服務(wù)器中的標(biāo)識(shí)為ETag: W/”ACXbWXd1n0CGMtAd65PcoA==”。
If-None-Match 如果瀏覽器在Cache-Control:max-age=60設(shè)置的時(shí)間超時(shí)后,發(fā)現(xiàn)消息頭中還設(shè)置了Etag值。然后,瀏覽器會(huì)再次向服務(wù)器請(qǐng)求數(shù)據(jù)并添加In-None-Match消息頭,它的值就是之前Etag值。服務(wù)器通過(guò)Etag來(lái)定位資源文件,根據(jù)它是否更新的情況給瀏覽器返回200或者是304。
Etag機(jī)制比Last-Modified精確度更高,如果兩者同時(shí)設(shè)置的話,Etag優(yōu)先級(jí)更高。
Pragma
Pragma頭域用來(lái)包含實(shí)現(xiàn)特定的指令,最常用的是Pragma:no-cache。
在HTTP/1.1協(xié)議中,它的含義和Cache- Control:no-cache相同。
以上是Http中關(guān)于緩存的相關(guān)信息。接下來(lái)我們進(jìn)入主題,如何配置OkHttp的緩存。
OKHTTP之Cache
OKHTTP如果要設(shè)置緩存,首要的條件就是設(shè)置一個(gè)緩存文件夾,在Android中為了安全起見(jiàn),一般設(shè)置為私密數(shù)據(jù)空間。通過(guò)getExternalCacheDir()獲取。如然后通過(guò)調(diào)用OKHttpClient.Builder中的cache()方法。如下面代碼所示:
//緩存文件夾
File cacheFile = new File(getExternalCacheDir().toString(),"cache");
//緩存大小為10M
int cacheSize = 10 * 1024 * 1024;
//創(chuàng)建緩存對(duì)象
Cache cache = new Cache(cacheFile,cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.build();
設(shè)置好Cache我們就可以正常訪問(wèn)了。我們可以通過(guò)獲取到的Response對(duì)象拿到它正常的消息和緩存的消息。
Response的消息有兩種類型,CacheResponse和NetworkResponse。CacheResponse代表從緩存取到的消息,NetworkResponse代表直接從服務(wù)端返回的消息。示例代碼如下:
private void testCache(){
//緩存文件夾
File cacheFile = new File(getExternalCacheDir().toString(),"cache");
//緩存大小為10M
int cacheSize = 10 * 1024 * 1024;
//創(chuàng)建緩存對(duì)象
final Cache cache = new Cache(cacheFile,cacheSize);
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.build();
//官方的一個(gè)示例的url
String url = "http://publicobject.com/helloworld.txt";
Request request = new Request.Builder()
.url(url)
.build();
Call call1 = client.newCall(request);
Response response1 = null;
try {
//第一次網(wǎng)絡(luò)請(qǐng)求
response1 = call1.execute();
Log.i(TAG, "testCache: response1 :"+response1.body().string());
Log.i(TAG, "testCache: response1 cache :"+response1.cacheResponse());
Log.i(TAG, "testCache: response1 network :"+response1.networkResponse());
response1.body().close();
} catch (IOException e) {
e.printStackTrace();
}
Call call12 = client.newCall(request);
try {
//第二次網(wǎng)絡(luò)請(qǐng)求
Response response2 = call12.execute();
Log.i(TAG, "testCache: response2 :"+response2.body().string());
Log.i(TAG, "testCache: response2 cache :"+response2.cacheResponse());
Log.i(TAG, "testCache: response2 network :"+response2.networkResponse());
Log.i(TAG, "testCache: response1 equals response2:"+response2.equals(response1));
response2.body().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
我們?cè)谏厦娴拇a中,用同一個(gè)url地址分別進(jìn)行了兩次網(wǎng)絡(luò)訪問(wèn),然后分別用Log打印它們的信息。
10-24 21:17:04.720 9901-17925/? I/SeniorActivity: testCache: response1 :
\\ //
\\ .ooo. //
.@@@@@@@@@.
:@@@@@@@@@@@@@:
:@@. '@@@@@' .@@:
@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@
:@@ :@@@@@@@@@@@@@@@@@. @@:
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@@@@@@@@@@@@@@@
'@@@@@@@@@@@@@@@'
@@@@ @@@@
@@@@ @@@@
@@@@ @@@@
'@@' '@@'
:@@@.
.@@@@@@@: +@@ `@@ @@` @@ @@
.@@@@'@@@@: +@@ `@@ @@` @@ @@
@@@ @@@ +@@ `@@ @@` @@ @@
.@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@
@@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@
@@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@
@@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+
@@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@#
@@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+
@@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@
@@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@
@@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@
@@:
@@:
@@:
10-24 21:17:04.720 9901-17925/? I/SeniorActivity: testCache: response1 cache :null
10-24 21:17:04.720 9901-17925/? I/SeniorActivity: testCache: response1 network :Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
10-24 21:17:05.031 9901-17925/? I/SeniorActivity: testCache: response2 :
\\ //
\\ .ooo. //
.@@@@@@@@@.
:@@@@@@@@@@@@@:
:@@. '@@@@@' .@@:
@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@
:@@ :@@@@@@@@@@@@@@@@@. @@:
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@ '@@@@@@@@@@@@@@@@@, @@@
@@@@@@@@@@@@@@@@@
'@@@@@@@@@@@@@@@'
@@@@ @@@@
@@@@ @@@@
@@@@ @@@@
'@@' '@@'
:@@@.
.@@@@@@@: +@@ `@@ @@` @@ @@
.@@@@'@@@@: +@@ `@@ @@` @@ @@
@@@ @@@ +@@ `@@ @@` @@ @@
.@@ @@: +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@;@@@@@
@@@ @@@ +@@ @@@ `@@ @@` @@@@@@ @@@@@@ @@@@@@@@@
@@@ @@@ +@@ @@@ `@@@@@@@@@@` @@ @@ @@@ :@@
@@@ @@@ +@@@@@ `@@@@@@@@@@` @@ @@ @@# @@+
@@@ @@@ +@@@@@+ `@@ @@` @@ @@ @@: @@#
@@: .@@` +@@@+@@ `@@ @@` @@ @@ @@# @@+
@@@. .@@@ +@@ @@@ `@@ @@` @@ @@ @@@ ,@@
@@@@@@@@@ +@@ @@@ `@@ @@` @@@@ @@@@ @@@@#@@@@
@@@@@@@ +@@ #@@ `@@ @@` @@@@: @@@@: @@'@@@@@
@@:
@@:
@@:
10-24 21:17:05.031 9901-17925/? I/SeniorActivity: testCache: response2 cache :Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
10-24 21:17:05.031 9901-17925/? I/SeniorActivity: testCache: response2 network :null
10-24 21:17:05.031 9901-17925/? I/SeniorActivity: testCache: response1 equals response2:false
打印的結(jié)果非常有意思是一個(gè)機(jī)器人和一個(gè)Okhttp的字符串。打印的結(jié)果主要說(shuō)明了一個(gè)現(xiàn)象,第一次訪問(wèn)的時(shí)候,Response的消息是NetworkResponse消息,此時(shí)CacheResponse的值為Null.而第二次訪問(wèn)的時(shí)候Response是CahceResponse,而此時(shí)NetworkResponse為空。也就說(shuō)明了上面的示例代碼能夠進(jìn)行網(wǎng)絡(luò)請(qǐng)求的緩存。
那么OKHTTP中的緩存就這么點(diǎn)內(nèi)容嗎?到此為至嗎?顯然不是。本篇文章開(kāi)頭講了大段的Http協(xié)議中的相關(guān)知識(shí)點(diǎn),貌似它們還沒(méi)有出現(xiàn)。
其實(shí)控制緩存的消息頭往往是服務(wù)端返回的信息中添加的如”Cache-Control:max-age=60”。所以,會(huì)有兩種情況。
- 客戶端和服務(wù)端開(kāi)發(fā)能夠很好溝通,按照達(dá)成一致的協(xié)議,服務(wù)端按照規(guī)定添加緩存相關(guān)的消息頭。
- 客戶端與服務(wù)端的開(kāi)發(fā)根本就不是同一家公司,沒(méi)有辦法也不可能要求服務(wù)端按照客戶端的意愿進(jìn)行開(kāi)發(fā)。
第一種辦法當(dāng)然很好,只要服務(wù)器在返回消息的時(shí)候添加好Cache-Control相關(guān)的消息便好。
第二種情況,就很麻煩,你真的無(wú)法左右別人的行為。怎么辦呢?好在OKHTTP能夠很輕易地處理這種情況。那就是定義一個(gè)攔截器,人為地添加Response中的消息頭,然后再傳遞給用戶,這樣用戶拿到的Response就有了我們理想當(dāng)中的消息頭Headers,從而達(dá)到控制緩存的意圖,正所謂移花接木。
緩存之?dāng)r截器
因?yàn)閿r截器可以拿到Request和Response,所以可以輕而易舉地加工這些東西。在這里我們?nèi)藶榈靥砑覥ache-Control消息頭。
class CacheInterceptor implements Interceptor{
@Override
public Response intercept(Chain chain) throws IOException {
Response originResponse = chain.proceed(chain.request());
//設(shè)置緩存時(shí)間為60秒,并移除了pragma消息頭,移除它的原因是因?yàn)閜ragma也是控制緩存的一個(gè)消息頭屬性
return originResponse.newBuilder().removeHeader("pragma")
.header("Cache-Control","max-age=60").build();
}
}
定義好攔截器中后,我們可以添加到OKHttpClient中了。
private void testCacheInterceptor(){
//緩存文件夾
File cacheFile = new File(getExternalCacheDir().toString(),"cache");
//緩存大小為10M
int cacheSize = 10 * 1024 * 1024;
//創(chuàng)建緩存對(duì)象
final Cache cache = new Cache(cacheFile,cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new CacheInterceptor())
.cache(cache)
.build();
.......
}
代碼后面部分有省略。主要通過(guò)在OkHttpClient.Builder()中addNetworkInterceptor()中添加。而這樣也挺簡(jiǎn)單的,就幾步完成了緩存代碼。
攔截器進(jìn)行緩存的缺點(diǎn)
網(wǎng)上有人說(shuō)用攔截器進(jìn)行緩存是野路子,是HOOK行為。這個(gè)我不大同意,前面我有分析過(guò)情況,如果客戶端能夠同服務(wù)端一起協(xié)商開(kāi)發(fā),當(dāng)然以服務(wù)器控制的緩存消息頭為準(zhǔn),但問(wèn)題在于你沒(méi)法這樣做。所以,能夠解決問(wèn)題才是最實(shí)在的。
好了,回到正題。用攔截器控制緩存有什么不好的地方呢?我們先看看下面的情況。
- 網(wǎng)絡(luò)訪問(wèn)請(qǐng)求的資源是文本信息,如新聞列表,這類信息經(jīng)常變動(dòng),一天更新好幾次,它們用的緩存時(shí)間應(yīng)該就很短。
- 網(wǎng)絡(luò)訪問(wèn)請(qǐng)求的資源是圖片或者視頻,它們變動(dòng)很少,或者是長(zhǎng)期不變動(dòng),那么它們用的緩存時(shí)間就應(yīng)該很長(zhǎng)。
那么,問(wèn)題來(lái)了。 因?yàn)镺KHTTP開(kāi)發(fā)建議是同一個(gè)APP,用同一個(gè)OKHTTPCLIENT對(duì)象這是為了只有一個(gè)緩存文件訪問(wèn)入口。這個(gè)很容易理解,單例模式嘛。但是問(wèn)題攔截器是在OKHttpClient.Builder當(dāng)中添加的。如果在攔截器中定義緩存的方法會(huì)導(dǎo)致圖片的緩存和新聞列表的緩存時(shí)間是一樣的,這顯然是不合理的,這屬于一刀切,就像這兩天專家說(shuō)的要把年收入12萬(wàn)元的人群劃分為高收入人群而不區(qū)別北上廣深的房?jī)r(jià)物價(jià)情況。真實(shí)的情況不應(yīng)該是圖片請(qǐng)求有它的緩存時(shí)間,新聞列表請(qǐng)求有它的緩存時(shí)間,應(yīng)該是每一個(gè)Request有它的緩存時(shí)間。 那么,有解決的方案嗎? 有的,okhttp官方有建議的方法。
okhttp官方文檔建議緩存方法
okhttp中建議用CacheControl這個(gè)類來(lái)進(jìn)行緩存策略的制定。 它內(nèi)部有兩個(gè)很重要的靜態(tài)實(shí)例。
/**強(qiáng)制使用網(wǎng)絡(luò)請(qǐng)求*/
public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();
/**
* 強(qiáng)制性使用本地緩存,如果本地緩存不滿足條件,則會(huì)返回code為504
*/
public static final CacheControl FORCE_CACHE = new Builder()
.onlyIfCached()
.maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
.build();
我們看到FORCE_NETWORK常量用來(lái)強(qiáng)制使用網(wǎng)絡(luò)請(qǐng)求。FORCE_CACHE只取本地的緩存。它們本身都是CacheControl對(duì)象,由內(nèi)部的Buidler對(duì)象構(gòu)造。下面我們來(lái)看看CacheControl.Builder
CacheControl.Builder
它有如下方法:
- noCache();//不使用緩存,用網(wǎng)絡(luò)請(qǐng)求
- noStore();//不使用緩存,也不存儲(chǔ)緩存
- onlyIfCached();//只使用緩存
- noTransform();//禁止轉(zhuǎn)碼
- maxAge(10, TimeUnit.MILLISECONDS);//設(shè)置超時(shí)時(shí)間為10ms。
- maxStale(10, TimeUnit.SECONDS);//超時(shí)之外的超時(shí)時(shí)間為10s
- minFresh(10, TimeUnit.SECONDS);//超時(shí)時(shí)間為當(dāng)前時(shí)間加上10秒鐘
知道了CacheControl的相關(guān)信息,那么它怎么使用呢?不同于攔截器設(shè)置緩存,CacheControl是針對(duì)Request的,所以它可以針對(duì)每個(gè)請(qǐng)求設(shè)置不同的緩存策略。比如圖片和新聞列表。下面代碼展示如何用CacheControl設(shè)置一個(gè)60秒的超時(shí)時(shí)間。
private void testCacheControl(){
//緩存文件夾
File cacheFile = new File(getExternalCacheDir().toString(),"cache");
//緩存大小為10M
int cacheSize = 10 * 1024 * 1024;
//創(chuàng)建緩存對(duì)象
final Cache cache = new Cache(cacheFile,cacheSize);
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.build();
//設(shè)置緩存時(shí)間為60秒
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(60, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url("http://blog.csdn.net/briblue")
.cacheControl(cacheControl)
.build();
try {
Response response = client.newCall(request).execute();
response.body().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
強(qiáng)制使用緩存
前面有講CacheControl.FORCE_CACHE這個(gè)常量。
public static final CacheControl FORCE_CACHE = new Builder()
.onlyIfCached()
.maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
.build();
它內(nèi)部其實(shí)就是調(diào)用onlyIfCached()和maxStale方法。
它的使用方法為
Request request = new Request.Builder()
.url("http://blog.csdn.net/briblue")
.cacheControl(Cache.FORCE_CACHE)
.build();
但是如前面后提到的,如果緩存不符合條件會(huì)返回504.這個(gè)時(shí)候我們要根據(jù)情況再進(jìn)行編碼,如緩存不行就再進(jìn)行一次網(wǎng)絡(luò)請(qǐng)求。
Response forceCacheResponse = client.newCall(request).execute();
if (forceCacheResponse.code() != 504) {
// 資源已經(jīng)緩存了,可以直接使用
} else {
// 資源沒(méi)有緩存,或者是緩存不符合條件了。
}
不使用緩存
前面也有講CacheControl.FORCE_NETWORK這個(gè)常量。
public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();
它的內(nèi)部其實(shí)是調(diào)用noCache()方法,也就是不緩存的意思。
它的使用方法為
Request request = new Request.Builder()
.url("http://blog.csdn.net/briblue")
.cacheControl(Cache.FORCE_NETWORK)
.build();
還有一種情況將maxAge設(shè)置為0,也不會(huì)取緩存,直接走網(wǎng)絡(luò)。
Request request = new Request.Builder()
.url("http://blog.csdn.net/briblue")
.cacheControl(new CacheControl.Builder()
.maxAge(0, TimeUnit.SECONDS))
.build();
總結(jié)
本文其實(shí)內(nèi)容不多,前面講了很多http協(xié)議下的緩存機(jī)制,我認(rèn)為是值得的,知道了Cache-Control這些定義,才能更好的懂得OKHTTP中的緩存設(shè)置。能夠明白為什么它要這樣做,為什么它可以這樣做。 最后歸納下要點(diǎn)
http協(xié)議下Cache-Control等消息頭的作用
okhttp如何用攔截器添加Cache-Control消息頭進(jìn)行緩存定制
okhttp如何用CacheControl進(jìn)行緩存的控制。
轉(zhuǎn)載至:http://blog.csdn.net/briblue/article/details/52920531
Ref
http://www.cnblogs.com/l1pe1/archive/2010/07/14/1777621.html
http://www.cnblogs.com/whoislcj/p/5537640.html