关闭

Volley 数据缓存分析

标签: 缓存http协议缓存机制
192人阅读 评论(0) 收藏 举报
分类:

1.缓存使用前提:

服务器必须支持,缓存,配置Cache-Control等头信息,因为Volley需要从这些头信息判断缓存是否已经过期。以及一些其他Header的参数

https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 http协议文档

预设问题:

1.缓存机制

2.是如何实现的-技术方案。

3.缓存位置(读/写方式)。

4.缓存更新方式!缓存多长时间

2 .Volley官方提供的流程图

这里写图片描述

                       (图1) volley缓存机制

Volley缓存实现,需要服务端进行配合,根据request—-response 中交互的 http中具体Header 字段。例如下图

这里写图片描述
(图2) Http请求的头
当然不止这一种:blog

HTTP头的Expires与Cache-control

request时用到:
| "no-cache"
| "no-store"
| "max-age" "=" delta-seconds
| "max-stale" [ "=" delta-seconds ]
| "min-fresh" "=" delta-seconds
| "no-transform"
| "only-if-cached"
| "cache-extension"
response时用到:
| "public"
| "private" [ "=" <"> field-name <"> ]
| "no-cache" [ "=" <"> field-name <"> ]
| "no-store"
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "=" delta-seconds
| "s-maxage" "=" delta-seconds
| "cache-extension"

具体细节下面进行分析

3.具体缓存存放的位置,是在首次初始化传入进去的。context.getCacheDir()。当然可以随意修改存储位置,做好相应的容错即可。

缓存字段主要在HttpHeaderParser 中进行定义,并进行包装内部类Entity结构

        long serverDate = 0;
        long lastModified = 0;
        long serverExpires = 0;
        long softExpire = 0;
        long finalExpire = 0;
        long maxAge = 0;
        long staleWhileRevalidate = 0;
        boolean hasCacheControl = false;
        boolean mustRevalidate = false;

当然这些字段都是可以在Http 协议中Header 找到对应的字段。缓存内容也是这些字段
Cache 接口的实现类有两个 NoCache 和 DiskBasedCache,前一个是空实现。DiskBasedCache进行具体的缓存逻辑

这里写图片描述

             (图3) Cache接口结构

在网络请求成功地方进行缓存,key= request url ,entry为具体response相应封装后的实体

/**
 * Puts the entry with the specified key into the cache.
 * 在网络请求成功中进行缓存
 */
@Override
public synchronized void put(String key, Entry entry) {
    pruneIfNeeded(entry.data.length);
    File file = getFileForKey(key);
    try {
        BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
        CacheHeader e = new CacheHeader(key, entry);
        boolean success = e.writeHeader(fos);
        if (!success) {
            fos.close();
            VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
            throw new IOException();
        }
        fos.write(entry.data);
        fos.close();
        putEntry(key, e);
        return;
    } catch (IOException e) {
    }
    boolean deleted = file.delete();
    if (!deleted) {
        VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
    }
}

1.首先进行判断缓存内容是否超过限制的最大容量,超过了就需要清除排在队列最前面的缓存数据
2.根据一些规则生成文件名,并以文件的形式进行返回
3.将key,entity 包装成DiskBasedCache 内部类 CacheHeader结构
4.通过OutputStream 序列化到本地文件中

下面是写入的方式

/**
 * Writes the contents of this CacheHeader to the specified OutputStream.
 */
public boolean writeHeader(OutputStream os) {
    try {
        writeInt(os, CACHE_MAGIC);
        writeString(os, key);
        writeString(os, etag == null ? "" : etag);
        writeLong(os, serverDate);
        writeLong(os, lastModified);
        writeLong(os, ttl);
        writeLong(os, softTtl);
        writeStringStringMap(responseHeaders, os);
        os.flush();
        return true;
    } catch (IOException e) {
        VolleyLog.d("%s", e.toString());
        return false;
    }
}

缓存的读取同样根据 key去拿对应的Entity内容。下面是读取缓存的源码

/**
 * Returns the cache entry with the specified key if it exists, null otherwise.
 */
@Override
public synchronized Entry get(String key) {
    CacheHeader entry = mEntries.get(key);
    // if the entry does not exist, return.
    if (entry == null) {
        return null;
    }

    File file = getFileForKey(key);
    CountingInputStream cis = null;
    try {
        cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
        CacheHeader.readHeader(cis); // eat header
        byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
        return entry.toCacheEntry(data);
    } catch (IOException e) {
        VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
        remove(key);
        return null;
    } finally {
        if (cis != null) {
            try {
                cis.close();
            } catch (IOException ioe) {
                return null;
            }
        }
    }
}

1.根据生成文件名的方式,拿到存入文件对应的路径,
2.BufferedInputStream 进行读流,然后逆序转换为Cache.Entity

下面是对缓存文件的源码

/**
 * Reads the header off of an InputStream and returns a CacheHeader object.
 *
 * @param is The InputStream to read from.
 * @throws IOException
 */
public static CacheHeader readHeader(InputStream is) throws IOException {
    CacheHeader entry = new CacheHeader();
    int magic = readInt(is);
    if (magic != CACHE_MAGIC) {
        // don't bother deleting, it'll get pruned eventually
        throw new IOException();
    }
    entry.key = readString(is);
    entry.etag = readString(is);
    if (entry.etag.equals("")) {
        entry.etag = null;
    }
    entry.serverDate = readLong(is);
    entry.lastModified = readLong(is);
    entry.ttl = readLong(is);
    entry.softTtl = readLong(is);
    entry.responseHeaders = readStringStringMap(is);

    return entry;
}

何时需要更新缓存,以及如何进行缓存清理。

/* True if a refresh is needed from the original data source. /
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}

根据服务端返回值,进行判断,是否需要刷新缓存。设置参数为true

/ Mark the response as intermediate.
response.intermediate = true;

Request时可以动态设置是否进行响应缓存

public final Request<?> setShouldCache(boolean shouldCache) {
    mShouldCache = shouldCache;
    return this;
}

引用1: https://segmentfault.com/q/1010000004711321 Volley源码中ImageLoader类的batchResponse方法的实现问题
引用2:http://blog.csdn.net/soft_po/article/details/51511546 Volley缓存机制

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:64749次
    • 积分:2190
    • 等级:
    • 排名:第17383名
    • 原创:142篇
    • 转载:18篇
    • 译文:5篇
    • 评论:20条
    博客专栏
    最新评论