Okhttp的緩存策略有如下幾種:
boolean noCache;//等于FORCE_NETWORK颖侄,不使用緩存
boolean noStore;//不緩存衣屏,不存儲(chǔ)
int maxAgeSeconds = -1;//緩存的有效時(shí)間
int maxStaleSeconds = -1;//緩存的延長有效時(shí)間
int minFreshSeconds = -1;//增加額外的緩存的有效時(shí)間,在stale前計(jì)算
boolean onlyIfCached;//等于FORCE_CACHE溯革,有緩存就用緩存
boolean noTransform;//不接受經(jīng)過轉(zhuǎn)碼的響應(yīng)
boolean immutable;//緩存有效時(shí)間內(nèi),響應(yīng)不會(huì)變化谷醉,避免服務(wù)器處理304響應(yīng)
如果這些緩存策略可以滿足需求致稀,那就不用再往下看了,相關(guān)的文章有很多
如果需要有網(wǎng)絡(luò)用網(wǎng)絡(luò)俱尼,無網(wǎng)絡(luò)(網(wǎng)絡(luò)請求失敹兜ァ)時(shí)使用緩存,或其他自定義策略,可以參考下面的方法
首先需要明確幾個(gè)要求
1.使用攔截器來實(shí)現(xiàn)矛绘,改動(dòng)小耍休,作用全局
2.方便控制每個(gè)接口是否緩存
3.接口參數(shù)有改動(dòng)時(shí)也能命中緩存
實(shí)現(xiàn)
一.存取
理想的情況下最好是將整個(gè)Response都存儲(chǔ)起來,但是看一眼源代碼
public final class Response implements Closeable
public interface Closeable extends AutoCloseable
public interface AutoCloseable
整個(gè)繼承和實(shí)現(xiàn)的鏈條里跟序列化沒有一毛錢關(guān)系货矮,并且是不能被繼承的final class
那既然不能保存Response喧锦,是否可以自己組裝一個(gè),答案是肯定的
Response通過Builder方式創(chuàng)建抓督,這個(gè)Builder也是public的可以調(diào)用到
public Builder() {
headers = new Headers.Builder();
}
那組裝時(shí)需要哪些參數(shù)呢燃少,點(diǎn)一下試試
emmm...有點(diǎn)多,以后再詳細(xì)說明這些參數(shù)本昏,先偷個(gè)懶供汛,可以先看一下哪里用到了這個(gè)Builder
這里有okhttp自己實(shí)現(xiàn)的緩存攔截器,進(jìn)去看看
return new Response.Builder()
.request(chain.request())//原始請求涌穆,這個(gè)在攔截器中可以直接拿到
.protocol(Protocol.HTTP_1_1)//協(xié)議怔昨,直接填就行
.code(504)//code碼,可以直接填200
.message("Unsatisfiable Request (only-if-cached)")//可以隨便寫宿稀,我這里寫得“use cache”便于以后統(tǒng)計(jì)
.body(Util.EMPTY_RESPONSE)//數(shù)據(jù)內(nèi)容*
.sentRequestAtMillis(-1L)//請求時(shí)間
.receivedResponseAtMillis(System.currentTimeMillis())//返回時(shí)間
.build();
這里有一個(gè)現(xiàn)成的例子
上面代碼中的*位置就是數(shù)據(jù)內(nèi)容保存的地方趁舀,這里需要添加一個(gè)ResponseBody類型的參數(shù),好在該類中有方便創(chuàng)建對象的方法
public static ResponseBody create(@Nullable MediaType contentType, String content)
這個(gè)方法有兩個(gè)參數(shù)祝沸,第一個(gè)是內(nèi)容類型矮烹,這個(gè)類中也有方便的方法
public static @Nullable MediaType parse(String string)//在一般返回的請求中直接使用MediaType.parse("application/json; charset=UTF-8")即可
第二個(gè)參數(shù)就是內(nèi)容主體了,就是服務(wù)端返回的數(shù)據(jù)內(nèi)容
構(gòu)造Response現(xiàn)在已經(jīng)清楚了罩锐,那現(xiàn)在就可以在保存緩存時(shí)只進(jìn)行數(shù)據(jù)內(nèi)容的保存即可奉狈,那應(yīng)該如何保存呢
這里可以使用下面的代碼獲取到body中的數(shù)據(jù)并轉(zhuǎn)為string類型
response.peekBody(Long.MAX_VALUE).string()
然后拿到這個(gè)數(shù)據(jù)可以用自己喜歡的方式進(jìn)行持久化保存
簡單使用可以用HashTable進(jìn)行<Url,data>鍵值對保存,然后將其這個(gè)map整個(gè)寫入到一個(gè)文件中涩惑,在使用時(shí)再整個(gè)讀取出來仁期,方便使用(或者使用xml,json等格式化數(shù)據(jù))竭恬,ps:為了安全起見只應(yīng)該保存Get方法的數(shù)據(jù)跛蛋,最好使用數(shù)據(jù)庫進(jìn)行持久化存儲(chǔ)
這里需要注意如果使用HashMap可能會(huì)出現(xiàn)java.util.ConcurrentModificationException異常
二.根據(jù)接口選擇是否緩存
這里的方法就很多了,說一個(gè)比較簡單的
可以在請求時(shí)添加一個(gè)臨時(shí)header痊硕,然后再攔截器中讀取這個(gè)header進(jìn)行辨認(rèn)并將這個(gè)header刪除(以免對請求產(chǎn)生不必要的影響)
如果使用retrofit進(jìn)行網(wǎng)絡(luò)請求赊级,這種方式在Retrofit中使用多個(gè)baseUrl有詳細(xì)說明
三.網(wǎng)絡(luò)請求失敗時(shí)命中緩存
這里只需要對攔截器中請求得部分進(jìn)行try-catch即可,在網(wǎng)絡(luò)請求失敗時(shí)okhttp會(huì)拋錯(cuò)岔绸,這時(shí)再catch塊中進(jìn)行緩存的返回即可
需要注意的是下面的集中情況需要過濾理逊,這是主動(dòng)打斷請求時(shí)會(huì)拋出的錯(cuò)誤
e.toString().contains("Canceled")
e.toString().contains("InterruptedIOException")
e.toString().contains("CANCEL")
四.在請求參數(shù)改變時(shí)仍然命中(忽略某些參數(shù))
這里需要對請求參數(shù)的改變有把握橡伞,比如net_type在使用移動(dòng)網(wǎng)絡(luò)的情況下應(yīng)該可以命中wifi時(shí)緩存的數(shù)據(jù)
其實(shí)沒啥可說的,將請求中的參數(shù)通過字符串分割的方式去掉其中的某些參數(shù)即可挡鞍,需要注意的是保存和獲取前的對比都需要去掉這些參數(shù)