美文网首页
Okhttp 缓存设计

Okhttp 缓存设计

作者: gczxbb | 来源:发表于2020-05-12 17:02 被阅读0次

    一、缓存接口

    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设计的缓存类,通过持有InternalCache匿名内部类对象访问Cache类缓存方法(同名方法)。final类型,未实现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希望使用内部设计的缓存。

    final Cache cache;
    final InternalCache internalCache;
    
    InternalCache internalCache() {
        return cache != null ? cache.internalCache : internalCache;
    }
    

    InternalCache接口暴露Cache类缓存的具体实现,如果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数组。
    数组中第0个Source,读取,封装Cache.Entry,由Entry构建Response。

    DiskLruCache.Entry实体创建。
    数组数量valueCount:ENTRY_COUNT=2。分别存METADATA和BODY。
    存储文件目录,在Cache的构造方法传入,Cache对象创建时机,在okhttpclient建造者builder中创建,自己定义存储目录。

    cleanFiles文件,创建Source。

    DiskLruCache.Entry创建时,初始化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;
        ....
    }
    

    根据请求,在Cache类中查找Response,创建缓存策略。
    工厂CacheStrategy.Factory类,CacheStrategy内部类,构造方法传入当前时间、Request请求、缓存Response。
    Factory工厂类get()方法创建CacheStrategy对象。

    public CacheStrategy get() {
        CacheStrategy candidate = getCandidate();
        if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
            return new CacheStrategy(null, null);
        }
        return candidate;
    }
    

    内部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);
        }
        ...
    }
    

    根据请求Request和缓存Response,生成策略对象CacheStragey。
    CacheStragey策略仅包含Request的情况。
    1,缓存Response是空。
    2,https协议请求,且Response的handshake是空。
    3,CacheControl设置为noStore状态,说明不存储,策略中仅包含Request。
    4,请求中包含noCache标志,或者请求中Header的If-Modified-Since或If-None-Match非空,策略中仅包含Request。
    5,当小于最大期限时,使用缓存,策略中仅包含Response。

    缓存策略创建后,根据内部networkRequest与cacheResponse是否存在,决定后续是否使用缓存。
    若无需网络访问,构建Response,返回。若需网络访问,继续链式调用,下一个拦截器是ConnectInterceptor,建立访问链路。最后,获取的Response视情况存入Cache。

    存储Request、cacheResponse以及从Response的Header解析的servedDate、expires。

    Request头部
    If-None-Match:加入上次访问服务器获取的etag(相当资源的hash),如果服务器资源未变,返回304,如果服务器资源改变,返回200。
    If-Modified-Since:把上次服务器告知的最后修改时间返回给服务器。如果在这个指定时间后未修改,返回200,否则304。

    Response头部
    Last-Modified:服务器文件的最后修改时间。
    Date:服务器返回请求发送的日期时间。

    1,缓存不存在
    2,https协议请求,无握手对象
    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中的时间,返回策略是一个响应超时的response,无request。
    7,解析response的header上次修改时间,etag,服务端时间,这些都没有,不存储。
    8,用这些重建request,加入到header中,绑定response
    If-None-Match,If-Modified-Since


    任重而道远

    相关文章

      网友评论

          本文标题:Okhttp 缓存设计

          本文链接:https://www.haomeiwen.com/subject/bitinhtx.html