一、緩存接口
InternalCache接口,定義一系列緩存方法。
public interface InternalCache {
Response get(Request request) throws IOException;
CacheRequest put(Response response) throws IOException;
void remove(Request request) throws IOException;
void update(Response cached, Response network);
void trackConditionalCacheHit();
void trackResponse(CacheStrategy cacheStrategy);
}
Cache類是okhttp設(shè)計的緩存類,通過持有InternalCache匿名內(nèi)部類對象訪問Cache類緩存方法(同名方法)。final類型,未實現(xiàn)InternalCache接口。
final InternalCache internalCache = new InternalCache() {
@Override
public Response get(Request request) throws IOException {
return Cache.this.get(request);
}
@Override
public CacheRequest put(Response response) throws IOException {
return Cache.this.put(response);
}
...
};
在緩存攔截器定義時,從OkHttpClient獲取InternalCache緩存對象。
interceptors.add(new CacheInterceptor(client.internalCache()));
OkHttpClient類中包含Cache和InternalCache,二者有一個是null,okhttp希望使用內(nèi)部設(shè)計的緩存。
final Cache cache;
final InternalCache internalCache;
InternalCache internalCache() {
return cache != null ? cache.internalCache : internalCache;
}
InternalCache接口暴露Cache類緩存的具體實現(xiàn),如果Cache是空,選擇InternalCache類對象。
二、存儲
Cache緩存采用DiskLruCache類存儲。
final DiskLruCache cache;
Cache(File directory, long maxSize, FileSystem fileSystem) {
this.cache = DiskLruCache.create(fileSystem, directory, VERSION, ENTRY_COUNT, maxSize);
}
LinkedHashMap<String, Entry>
DiskLruCache.Entry實體,Snapshot快照包含key、Source數(shù)組。
數(shù)組中第0個Source,讀取,封裝Cache.Entry,由Entry構(gòu)建Response。
DiskLruCache.Entry實體創(chuàng)建。
數(shù)組數(shù)量valueCount:ENTRY_COUNT=2。分別存METADATA和BODY。
存儲文件目錄,在Cache的構(gòu)造方法傳入,Cache對象創(chuàng)建時機,在okhttpclient建造者builder中創(chuàng)建,自己定義存儲目錄。
cleanFiles文件,創(chuàng)建Source。
DiskLruCache.Entry創(chuàng)建時,初始化cleanFiles文件,文件名:directory目錄?+key。
match匹配request和response。
url md5作為key。
三、策略
CacheInterceptor攔截器
@Override
public Response intercept(Chain chain) throws IOException{
Response cacheCandidate = cache != null
? cache.get(chain.request())
: null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
....
}
根據(jù)請求,在Cache類中查找Response,創(chuàng)建緩存策略。
工廠CacheStrategy.Factory類,CacheStrategy內(nèi)部類,構(gòu)造方法傳入當前時間、Request請求、緩存Response。
Factory工廠類get()方法創(chuàng)建CacheStrategy對象。
public CacheStrategy get() {
CacheStrategy candidate = getCandidate();
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
return new CacheStrategy(null, null);
}
return candidate;
}
內(nèi)部getCandidate()方法。
private CacheStrategy getCandidate() {
if (cacheResponse == null) {//1
return new CacheStrategy(request, null);
}
if (request.isHttps() && cacheResponse.handshake() == null) {//2
return new CacheStrategy(request, null);
}
if (!isCacheable(cacheResponse, request)) {//3
return new CacheStrategy(request, null);
}
CacheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {//4
return new CacheStrategy(request, null);
}
...
}
根據(jù)請求Request和緩存Response,生成策略對象CacheStragey。
CacheStragey策略僅包含Request的情況。
1,緩存Response是空。
2,https協(xié)議請求,且Response的handshake是空。
3,CacheControl設(shè)置為noStore狀態(tài),說明不存儲,策略中僅包含Request。
4,請求中包含noCache標志,或者請求中Header的If-Modified-Since或If-None-Match非空,策略中僅包含Request。
5,當小于最大期限時,使用緩存,策略中僅包含Response。
緩存策略創(chuàng)建后,根據(jù)內(nèi)部networkRequest與cacheResponse是否存在,決定后續(xù)是否使用緩存。
若無需網(wǎng)絡(luò)訪問,構(gòu)建Response,返回。若需網(wǎng)絡(luò)訪問,繼續(xù)鏈式調(diào)用,下一個攔截器是ConnectInterceptor,建立訪問鏈路。最后,獲取的Response視情況存入Cache。
存儲Request、cacheResponse以及從Response的Header解析的servedDate、expires。
Request頭部
If-None-Match:加入上次訪問服務(wù)器獲取的etag(相當資源的hash),如果服務(wù)器資源未變,返回304,如果服務(wù)器資源改變,返回200。
If-Modified-Since:把上次服務(wù)器告知的最后修改時間返回給服務(wù)器。如果在這個指定時間后未修改,返回200,否則304。
Response頭部
Last-Modified:服務(wù)器文件的最后修改時間。
Date:服務(wù)器返回請求發(fā)送的日期時間。
1,緩存不存在
2,https協(xié)議請求,無握手對象
3,針對reponse的code,,非白名單的code,不存儲,response和request中的nostore標志都是false,才會存儲,有一個nostore不存儲,就不會存儲。
白名單:200,203,204,300.
4,request的cacheControl,noCache不存儲,或請求header有"If-Modified-Since或If-None-Match,不存儲
5,response的CacheControl,是immutable,返回的策略request是空。
6,當responseCaching.noCache是false時,比較request中的時間,返回策略是一個響應(yīng)超時的response,無request。
7,解析response的header上次修改時間,etag,服務(wù)端時間,這些都沒有,不存儲。
8,用這些重建request,加入到header中,綁定response
If-None-Match,If-Modified-Since
任重而道遠