zoukankan      html  css  js  c++  java
  • Volley Cache机制分析

    1.http缓存机制

    要弄明白volley缓存机制,那么肯定是和浏览器的缓存机制有关了,简单来说volley整套框架要做的事都是模拟浏览器来进行一次次的http交互

    1.1.概述

    http缓存的是指当Web请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是从原始服务器中提取这个文档。

    1.2.与缓存有关的头信息

    1.2.1.request:

    • Cache-Control: max-age=0 以秒为单位
    • If-Modified-Since: Mon, 19 Nov 2012 08:38:01 GMT 缓存文件的最后修改时间。
    • If-None-Match: "0693f67a67cc1:0" 缓存文件的Etag值
    • Cache-Control: no-cache 不使用缓存
    • Pragma: no-cache 不使用缓存

    1.2.2.response:

    • Cache-Control: public 响应被缓存,并且在多用户间共享
    • Cache-Control: private 响应只能作为私有缓存,不能在用户之间共享
    • Cache-Control:no-cache 提醒浏览器要从服务器提取文档进行验证
    • Cache-Control:no-store 绝对禁止缓存(用于机密,敏感文件)
    • Cache-Control: max-age=60 60秒之后缓存过期(相对时间)
    • Date: Mon, 19 Nov 2012 08:39:00 GMT 当前response发送的时间
    • Expires: Mon, 19 Nov 2012 08:40:01 GMT 缓存过期的时间(绝对时间)
    • Last-Modified: Mon, 19 Nov 2012 08:38:01 GMT 服务器端文件的最后修改时间
    • ETag: "20b1add7ec1cd1:0" 服务器端文件的Etag值

    如果同时存在cache-control和Expires怎么办呢?

    优先使用cache-control,如果没有cache-control才考虑Expires

    2.Volley缓存机制

    1.当你add进来一个request的时候,其会根据request.shouldCache(默认为true)进行分发决定是交给NetworkDispatcher还是CacheDispatcher处理

    2.NetworkDispatcher通过Network请求数据, 如果有缓存的头信息,会一起发送给服务器

        // Perform the network request.
     NetworkResponse networkResponse = mNetwork.performRequest(request);
    
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
            long requestStart = SystemClock.elapsedRealtime();
            while (true) {
                    .....
                    .....
                    .....
                    addCacheHeaders(headers, request.getCacheEntry());
                    .....
                    .....
                    .....
            }
        }
    

    3.解析NetworkResponse

     Response<?> response = request.parseNetworkResponse(networkResponse);
    

    在这,得在自定义的request中调用很重要的一个静态方法

    HttpHeaderParser.parseCacheHeaders(response)

    这个方法主要是将NetworkResponse进行封装成一个Cache.Entry对象

     public static Cache.Entry  parseCacheHeaders(NetworkResponse response) {
            //当前系统时间
            long now = System.currentTimeMillis();
            Map<String, String> headers = response.headers;
            long serverDate = 0;
            long serverExpires = 0;
            long softExpire = 0;
            long maxAge = 0;
            boolean hasCacheControl = false;
            String serverEtag = null;
            String headerValue;
            headerValue = headers.get("Date");
            if (headerValue != null) {
                //服务器时间
                serverDate = parseDateAsEpoch(headerValue);
            }
            //获取Cache-Control信息
            headerValue = headers.get("Cache-Control");
            if (headerValue != null) {
                hasCacheControl = true;
                String[] tokens = headerValue.split(",");
                for (int i = 0; i < tokens.length; i++) {
                    String token = tokens[i].trim();
              if (token.equals("no-cache") || token.equals("no-store")) {
                        //不缓存
                        return null;
                    } else if (token.startsWith("max-age=")) {
                        try {
                            //缓存过期时间(相对时间)
                            maxAge = Long.parseLong(token.substring(8));
                        } catch (Exception e) {
                        }
                    } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
                        maxAge = 0;
                    }
                }
            }
            headerValue = headers.get("Expires");
            if (headerValue != null) {
                //过期时间(绝对时间)
                serverExpires = parseDateAsEpoch(headerValue);
            }
            //ETag
            serverEtag = headers.get("ETag");
            // Cache-Control takes precedence over an Expires header, even if both exist and Expires
            // is more restrictive.
            if (hasCacheControl) {
                //软件过期时间
                softExpire = now + maxAge * 1000;
            } else if (serverDate > 0 && serverExpires >= serverDate) {
        // Default semantic for Expire header in HTTP specification is softExpire.
                softExpire = now + (serverExpires - serverDate);
            }
            Cache.Entry entry = new Cache.Entry();
            entry.data = response.data;
            entry.etag = serverEtag;
            entry.softTtl = softExpire;
            entry.ttl = entry.softTtl;
            entry.serverDate = serverDate;
            entry.responseHeaders = headers;
            return entry;
        }
    
    

    4.在NetworkDispatcher中,再根据其shouldCache和是否有缓存实体来判断是否要进行缓存操作

        if (request.shouldCache() && response.cacheEntry != null) {
           mCache.put(request.getCacheKey(), response.cacheEntry);
       request.addMarker("network-cache-written"); }
    

    5.CacheDispatcher的处理

    当收到一个request之后,会先到Cache里面去取,看下是否有缓存,
    当出现没有缓存 和 缓存过期的情况就直接丢给NetworkDispatcher来处理;
    如果缓存没过期,直接拿到缓存实体丢给request的parseNetworkResponse方法这里调用就和NetworkDispatcher里面处理差不多了;

     @Override
        public void run() {
            if (DEBUG) VolleyLog.v("start new dispatcher");
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    
    // Make a blocking call to initialize the cache.
            mCache.initialize();
            while (true) {
                try {
        // Get a request from the cache triage queue, blocking until
                    // at least one is available.
                    final Request<?> request = mCacheQueue.take();
                    request.addMarker("cache-queue-take");
         // If the request has been canceled, don't bother dispatching it.
                    if (request.isCanceled()) {
                        request.finish("cache-discard-canceled");
                        continue;
                    }
                    // Attempt to retrieve this item from cache.
                    Cache.Entry entry = mCache.get(request.getCacheKey());
                    if (entry == null) {
                        request.addMarker("cache-miss");
                        // Cache miss; send off to the network dispatcher.
                        mNetworkQueue.put(request);
                        continue;
                    }
        // If it is completely expired, just send it to the network.
                    if (entry.isExpired()) {
                        request.addMarker("cache-hit-expired");
                        request.setCacheEntry(entry);
                        mNetworkQueue.put(request);
                        continue;
                    }
                    // We have a cache hit; parse its data for delivery back to the request.
                    request.addMarker("cache-hit");
                    Response<?> response = request.parseNetworkResponse(
                            new NetworkResponse(entry.data, entry.responseHeaders));
                    request.addMarker("cache-hit-parsed");
                    if (!entry.refreshNeeded()) {
              // Completely unexpired cache hit. Just deliver the response.
                        mDelivery.postResponse(request, response);
                    } else {
            // Soft-expired cache hit. We can deliver the cached response,
            // but we need to also send the request to the network for
                        // refreshing.
                        request.addMarker("cache-hit-refresh-needed");
                        request.setCacheEntry(entry);
                        // Mark the response as intermediate.
                        response.intermediate = true;
             // Post the intermediate response back to the user and have
             // the delivery then forward the request along to the network.
              mDelivery.postResponse(request, response, new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mNetworkQueue.put(request);
                                } catch (InterruptedException e) {
                                    // Not much we can do about this.
                                }
                            }
                        });
                    }
                } catch (InterruptedException e) {
             // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
            }
        }
    
  • 相关阅读:
    别让你的生活止于平庸!(摘)
    NSURLSession 请求
    第三方原理
    iOS实用的小技巧
    简书APP
    网络请求
    JQuery 简介
    struts2拦截器的实现原理及源码剖析
    hibernate配置文件注意点
    hibernate中三种状态
  • 原文地址:https://www.cnblogs.com/adison/p/4039312.html
Copyright © 2011-2022 走看看