FileDocCategorySizeDatePackage
DiskBasedCache.javaAPI DocAndroid 5.1 API18040Thu Mar 12 22:22:56 GMT 2015com.android.volley.toolbox

DiskBasedCache

public class DiskBasedCache extends Object implements com.android.volley.Cache
Cache implementation that caches files directly onto the hard disk in the specified directory. The default disk usage size is 5MB, but is configurable.

Fields Summary
private final Map
mEntries
Map of the Key, CacheHeader pairs
private long
mTotalSize
Total amount of space currently used by the cache in bytes.
private final File
mRootDirectory
The root directory to use for the cache.
private final int
mMaxCacheSizeInBytes
The maximum size of the cache in bytes.
private static final int
DEFAULT_DISK_USAGE_BYTES
Default maximum disk usage in bytes.
private static final float
HYSTERESIS_FACTOR
High water mark percentage for the cache
private static final int
CACHE_MAGIC
Magic number for current version of cache file format.
Constructors Summary
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes)
Constructs an instance of the DiskBasedCache at the specified directory.

param
rootDirectory The root directory of the cache.
param
maxCacheSizeInBytes The maximum size of the cache in bytes.


                                     
         
        mRootDirectory = rootDirectory;
        mMaxCacheSizeInBytes = maxCacheSizeInBytes;
    
public DiskBasedCache(File rootDirectory)
Constructs an instance of the DiskBasedCache at the specified directory using the default maximum cache size of 5MB.

param
rootDirectory The root directory of the cache.

        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
    
Methods Summary
public synchronized voidclear()
Clears the cache. Deletes all cached files from disk.

        File[] files = mRootDirectory.listFiles();
        if (files != null) {
            for (File file : files) {
                file.delete();
            }
        }
        mEntries.clear();
        mTotalSize = 0;
        VolleyLog.d("Cache cleared.");
    
public synchronized Entryget(java.lang.String key)
Returns the cache entry with the specified key if it exists, null otherwise.

        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 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;
                }
            }
        }
    
public java.io.FilegetFileForKey(java.lang.String key)
Returns a file object for the given cache key.

        return new File(mRootDirectory, getFilenameForKey(key));
    
private java.lang.StringgetFilenameForKey(java.lang.String key)
Creates a pseudo-unique filename for the specified cache key.

param
key The key to generate a file name for.
return
A pseudo-unique filename.

        int firstHalfLength = key.length() / 2;
        String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
        localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
        return localFilename;
    
public synchronized voidinitialize()
Initializes the DiskBasedCache by scanning for all files currently in the specified root directory. Creates the root directory if necessary.

        if (!mRootDirectory.exists()) {
            if (!mRootDirectory.mkdirs()) {
                VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
            }
            return;
        }

        File[] files = mRootDirectory.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                CacheHeader entry = CacheHeader.readHeader(fis);
                entry.size = file.length();
                putEntry(entry.key, entry);
            } catch (IOException e) {
                if (file != null) {
                   file.delete();
                }
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException ignored) { }
            }
        }
    
public synchronized voidinvalidate(java.lang.String key, boolean fullExpire)
Invalidates an entry in the cache.

param
key Cache key
param
fullExpire True to fully expire the entry, false to soft expire

        Entry entry = get(key);
        if (entry != null) {
            entry.softTtl = 0;
            if (fullExpire) {
                entry.ttl = 0;
            }
            put(key, entry);
        }

    
private voidpruneIfNeeded(int neededSpace)
Prunes the cache to fit the amount of bytes specified.

param
neededSpace The amount of bytes we are trying to fit into the cache.

        if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
            return;
        }
        if (VolleyLog.DEBUG) {
            VolleyLog.v("Pruning old cache entries.");
        }

        long before = mTotalSize;
        int prunedFiles = 0;
        long startTime = SystemClock.elapsedRealtime();

        Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, CacheHeader> entry = iterator.next();
            CacheHeader e = entry.getValue();
            boolean deleted = getFileForKey(e.key).delete();
            if (deleted) {
                mTotalSize -= e.size;
            } else {
               VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
                       e.key, getFilenameForKey(e.key));
            }
            iterator.remove();
            prunedFiles++;

            if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
                break;
            }
        }

        if (VolleyLog.DEBUG) {
            VolleyLog.v("pruned %d files, %d bytes, %d ms",
                    prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);
        }
    
public synchronized voidput(java.lang.String key, Entry entry)
Puts the entry with the specified key into the cache.

        pruneIfNeeded(entry.data.length);
        File file = getFileForKey(key);
        try {
            FileOutputStream fos = 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());
        }
    
private voidputEntry(java.lang.String key, com.android.volley.toolbox.DiskBasedCache$CacheHeader entry)
Puts the entry with the specified key into the cache.

param
key The key to identify the entry by.
param
entry The entry to cache.

        if (!mEntries.containsKey(key)) {
            mTotalSize += entry.size;
        } else {
            CacheHeader oldEntry = mEntries.get(key);
            mTotalSize += (entry.size - oldEntry.size);
        }
        mEntries.put(key, entry);
    
private static intread(java.io.InputStream is)
Simple wrapper around {@link InputStream#read()} that throws EOFException instead of returning -1.

        int b = is.read();
        if (b == -1) {
            throw new EOFException();
        }
        return b;
    
static intreadInt(java.io.InputStream is)

        int n = 0;
        n |= (read(is) << 0);
        n |= (read(is) << 8);
        n |= (read(is) << 16);
        n |= (read(is) << 24);
        return n;
    
static longreadLong(java.io.InputStream is)

        long n = 0;
        n |= ((read(is) & 0xFFL) << 0);
        n |= ((read(is) & 0xFFL) << 8);
        n |= ((read(is) & 0xFFL) << 16);
        n |= ((read(is) & 0xFFL) << 24);
        n |= ((read(is) & 0xFFL) << 32);
        n |= ((read(is) & 0xFFL) << 40);
        n |= ((read(is) & 0xFFL) << 48);
        n |= ((read(is) & 0xFFL) << 56);
        return n;
    
static java.lang.StringreadString(java.io.InputStream is)

        int n = (int) readLong(is);
        byte[] b = streamToBytes(is, n);
        return new String(b, "UTF-8");
    
static java.util.MapreadStringStringMap(java.io.InputStream is)

        int size = readInt(is);
        Map<String, String> result = (size == 0)
                ? Collections.<String, String>emptyMap()
                : new HashMap<String, String>(size);
        for (int i = 0; i < size; i++) {
            String key = readString(is).intern();
            String value = readString(is).intern();
            result.put(key, value);
        }
        return result;
    
public synchronized voidremove(java.lang.String key)
Removes the specified key from the cache if it exists.

        boolean deleted = getFileForKey(key).delete();
        removeEntry(key);
        if (!deleted) {
            VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
                    key, getFilenameForKey(key));
        }
    
private voidremoveEntry(java.lang.String key)
Removes the entry identified by 'key' from the cache.

        CacheHeader entry = mEntries.get(key);
        if (entry != null) {
            mTotalSize -= entry.size;
            mEntries.remove(key);
        }
    
private static byte[]streamToBytes(java.io.InputStream in, int length)
Reads the contents of an InputStream into a byte[].

        byte[] bytes = new byte[length];
        int count;
        int pos = 0;
        while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) {
            pos += count;
        }
        if (pos != length) {
            throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
        }
        return bytes;
    
static voidwriteInt(java.io.OutputStream os, int n)

        os.write((n >> 0) & 0xff);
        os.write((n >> 8) & 0xff);
        os.write((n >> 16) & 0xff);
        os.write((n >> 24) & 0xff);
    
static voidwriteLong(java.io.OutputStream os, long n)

        os.write((byte)(n >>> 0));
        os.write((byte)(n >>> 8));
        os.write((byte)(n >>> 16));
        os.write((byte)(n >>> 24));
        os.write((byte)(n >>> 32));
        os.write((byte)(n >>> 40));
        os.write((byte)(n >>> 48));
        os.write((byte)(n >>> 56));
    
static voidwriteString(java.io.OutputStream os, java.lang.String s)

        byte[] b = s.getBytes("UTF-8");
        writeLong(os, b.length);
        os.write(b, 0, b.length);
    
static voidwriteStringStringMap(java.util.Map map, java.io.OutputStream os)

        if (map != null) {
            writeInt(os, map.size());
            for (Map.Entry<String, String> entry : map.entrySet()) {
                writeString(os, entry.getKey());
                writeString(os, entry.getValue());
            }
        } else {
            writeInt(os, 0);
        }