Okhttp 緩存設(shè)計


一、緩存接口

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


任重而道遠

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容