DiskBasedCache.java revision e48f4430bfd3030350aa5ba827b449c37e2fadc9
1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/* 2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project 3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License"); 5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License. 6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at 7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * http://www.apache.org/licenses/LICENSE-2.0 9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software 11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS, 12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and 14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License. 15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley.toolbox; 18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 19d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.os.SystemClock; 20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 21d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Cache; 22d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.VolleyLog; 23d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 24d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.File; 25d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.FileInputStream; 26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.FileOutputStream; 27d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.FilterInputStream; 28d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.IOException; 29d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.InputStream; 30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.ObjectInputStream; 31d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.ObjectOutputStream; 32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.io.OutputStream; 33e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport java.util.Collections; 34e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queruimport java.util.HashMap; 35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Iterator; 36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.LinkedHashMap; 37d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport java.util.Map; 38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/** 40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Cache implementation that caches files directly onto the hard disk in the specified 41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * directory. The default disk usage size is 5MB, but is configurable. 42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class DiskBasedCache implements Cache { 44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Map of the Key, CacheHeader pairs */ 46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final Map<String, CacheHeader> mEntries = 47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru new LinkedHashMap<String, CacheHeader>(16, .75f, true); 48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 49d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Total amount of space currently used by the cache in bytes. */ 50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private long mTotalSize = 0; 51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The root directory to use for the cache. */ 53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final File mRootDirectory; 54d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The maximum size of the cache in bytes. */ 56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final int mMaxCacheSizeInBytes; 57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Default maximum disk usage in bytes. */ 59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024; 60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** High water mark percentage for the cache */ 62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static final float HYSTERESIS_FACTOR = 0.9f; 63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Current cache version */ 65e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru private static final int CACHE_VERSION = 2; 66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 68d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Constructs an instance of the DiskBasedCache at the specified directory. 69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param rootDirectory The root directory of the cache. 70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param maxCacheSizeInBytes The maximum size of the cache in bytes. 71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { 73d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mRootDirectory = rootDirectory; 74d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mMaxCacheSizeInBytes = maxCacheSizeInBytes; 75d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 76d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 77d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Constructs an instance of the DiskBasedCache at the specified directory using 79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * the default maximum cache size of 5MB. 80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param rootDirectory The root directory of the cache. 81d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public DiskBasedCache(File rootDirectory) { 83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); 84d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 85d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 86d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 87d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Clears the cache. Deletes all cached files from disk. 88d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 89d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 90d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public synchronized void clear() { 91d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru File[] files = mRootDirectory.listFiles(); 92d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (files != null) { 93d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru for (File file : files) { 94d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru file.delete(); 95d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 96d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 97d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mEntries.clear(); 98d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mTotalSize = 0; 99d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.d("Cache cleared."); 100d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 101d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 102d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 103d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Returns the cache entry with the specified key if it exists, null otherwise. 104d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 105d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 106d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public synchronized Entry get(String key) { 107d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader entry = mEntries.get(key); 108d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // if the entry does not exist, return. 109d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (entry == null) { 110d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return null; 111d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 112d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 113d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru File file = getFileForKey(key); 114d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CountingInputStream cis = null; 115d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 116d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru cis = new CountingInputStream(new FileInputStream(file)); 117d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader.readHeader(cis); // eat header 118d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); 119d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return entry.toCacheEntry(data); 120d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (IOException e) { 121d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString()); 122d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru remove(key); 123d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return null; 124d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } finally { 125d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (cis != null) { 126d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 127d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru cis.close(); 128d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (IOException ioe) { 129d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return null; 130d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 131d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 132d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 133d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 134d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 135d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 136d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Initializes the DiskBasedCache by scanning for all files currently in the 137d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * specified root directory. 138d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 139d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 140d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public synchronized void initialize() { 141d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru File[] files = mRootDirectory.listFiles(); 142d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (files == null) { 143d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return; 144d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 145d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru for (File file : files) { 146d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru FileInputStream fis = null; 147d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 148d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru fis = new FileInputStream(file); 149d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader entry = CacheHeader.readHeader(fis); 150d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.size = file.length(); 151d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru putEntry(entry.key, entry); 152d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (IOException e) { 153d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (file != null) { 154d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru file.delete(); 155d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 156d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } finally { 157d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 158d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (fis != null) { 159d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru fis.close(); 160d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 161d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (IOException ignored) { } 162d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 163d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 164d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 165d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 166d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 167d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Invalidates an entry in the cache. 168d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param key Cache key 169d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param fullExpire True to fully expire the entry, false to soft expire 170d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 171d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 172d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public synchronized void invalidate(String key, boolean fullExpire) { 173d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Entry entry = get(key); 174d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (entry != null) { 175d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.softTtl = 0; 176d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (fullExpire) { 177d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.ttl = 0; 178d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 179d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru put(key, entry); 180d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 181d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 182d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 183d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 184d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 185d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Puts the entry with the specified key into the cache. 186d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 187d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 188d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public synchronized void put(String key, Entry entry) { 189d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru pruneIfNeeded(entry.data.length); 190d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru File file = getFileForKey(key); 191d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 192d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru FileOutputStream fos = new FileOutputStream(file); 193d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader e = new CacheHeader(key, entry); 194d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.writeHeader(fos); 195d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru fos.write(entry.data); 196d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru fos.close(); 197d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru putEntry(key, e); 198d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return; 199d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (IOException e) { 200d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 201d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru boolean deleted = file.delete(); 202d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (!deleted) { 203d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.d("Could not clean up file %s", file.getAbsolutePath()); 204d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 205d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 206d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 207d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 208d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Removes the specified key from the cache if it exists. 209d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 210d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 211d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public synchronized void remove(String key) { 212d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru boolean deleted = getFileForKey(key).delete(); 213d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru removeEntry(key); 214d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (!deleted) { 215d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", 216d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru key, getFilenameForKey(key)); 217d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 218d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 219d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 220d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 221d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Creates a pseudo-unique filename for the specified cache key. 222d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param key The key to generate a file name for. 223d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @return A pseudo-unique filename. 224d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 225d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private String getFilenameForKey(String key) { 226d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int firstHalfLength = key.length() / 2; 227d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode()); 228d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru localFilename += String.valueOf(key.substring(firstHalfLength).hashCode()); 229d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return localFilename; 230d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 231d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 232d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 233d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Returns a file object for the given cache key. 234d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 235d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public File getFileForKey(String key) { 236d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return new File(mRootDirectory, getFilenameForKey(key)); 237d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 238d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 239d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 240d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Prunes the cache to fit the amount of bytes specified. 241d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param neededSpace The amount of bytes we are trying to fit into the cache. 242d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 243d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private void pruneIfNeeded(int neededSpace) { 244d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { 245d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return; 246d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 247d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (VolleyLog.DEBUG) { 248d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.v("Pruning old cache entries."); 249d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 250d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 251d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru long before = mTotalSize; 252d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int prunedFiles = 0; 253d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru long startTime = SystemClock.elapsedRealtime(); 254d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 255d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator(); 256d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru while (iterator.hasNext()) { 257d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Map.Entry<String, CacheHeader> entry = iterator.next(); 258d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader e = entry.getValue(); 259d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru boolean deleted = getFileForKey(e.key).delete(); 260d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (deleted) { 261d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mTotalSize -= e.size; 262d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } else { 263d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", 264d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.key, getFilenameForKey(e.key)); 265d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 266d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru iterator.remove(); 267d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru prunedFiles++; 268d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 269d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) { 270d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru break; 271d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 272d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 273d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 274d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (VolleyLog.DEBUG) { 275d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.v("pruned %d files, %d bytes, %d ms", 276d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime); 277d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 278d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 279d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 280d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 281d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Puts the entry with the specified key into the cache. 282d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param key The key to identify the entry by. 283d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param entry The entry to cache. 284d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 285d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private void putEntry(String key, CacheHeader entry) { 286d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (!mEntries.containsKey(key)) { 287d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mTotalSize += entry.size; 288d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } else { 289d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader oldEntry = mEntries.get(key); 290d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mTotalSize += (entry.size - oldEntry.size); 291d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 292d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mEntries.put(key, entry); 293d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 294d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 295d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 296d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Removes the entry identified by 'key' from the cache. 297d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 298d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private void removeEntry(String key) { 299d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader entry = mEntries.get(key); 300d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (entry != null) { 301d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mTotalSize -= entry.size; 302d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mEntries.remove(key); 303d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 304d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 305d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 306d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 307d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Reads the contents of an InputStream into a byte[]. 308d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * */ 309d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static byte[] streamToBytes(InputStream in, int length) throws IOException { 310d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru byte[] bytes = new byte[length]; 311d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int count; 312d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int pos = 0; 313d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) { 314d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru pos += count; 315d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 316d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (pos != length) { 317d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru throw new IOException("Expected " + length + " bytes, read " + pos + " bytes"); 318d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 319d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return bytes; 320d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 321d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 322d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 323d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Handles holding onto the cache headers for an entry. 324d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 325d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static class CacheHeader { 326d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The size of the data identified by this CacheHeader. (This is not 327d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * serialized to disk. */ 328d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public long size; 329d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 330d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** The key that identifies the cache entry. */ 331d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public String key; 332d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 333d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** ETag for cache coherence. */ 334d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public String etag; 335d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 336d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Date of this response as reported by the server. */ 337d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public long serverDate; 338d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 339d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** TTL for this record. */ 340d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public long ttl; 341d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 342d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Soft TTL for this record. */ 343d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public long softTtl; 344d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 345e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru /** Headers from the response resulting in this cache entry. */ 346e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru public Map<String, String> responseHeaders; 347e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 348d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private CacheHeader() { } 349d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 350d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 351d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Instantiates a new CacheHeader object 352d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param key The key that identifies the cache entry 353d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param entry The cache entry. 354d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 355d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public CacheHeader(String key, Entry entry) { 356d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this.key = key; 357d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this.size = entry.data.length; 358d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this.etag = entry.etag; 359d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this.serverDate = entry.serverDate; 360d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this.ttl = entry.ttl; 361d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru this.softTtl = entry.softTtl; 362e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru this.responseHeaders = entry.responseHeaders; 363d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 364d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 365d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 366d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Reads the header off of an InputStream and returns a CacheHeader object. 367d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param is The InputStream to read from. 368d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @throws IOException 369d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 370d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public static CacheHeader readHeader(InputStream is) throws IOException { 371d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru CacheHeader entry = new CacheHeader(); 372d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru ObjectInputStream ois = new ObjectInputStream(is); 373d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int version = ois.readByte(); 374d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (version != CACHE_VERSION) { 375d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // don't bother deleting, it'll get pruned eventually 376d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru throw new IOException(); 377d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 378d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.key = ois.readUTF(); 379d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.etag = ois.readUTF(); 380d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (entry.etag.equals("")) { 381d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.etag = null; 382d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 383d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.serverDate = ois.readLong(); 384d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.ttl = ois.readLong(); 385d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru entry.softTtl = ois.readLong(); 386e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru entry.responseHeaders = readStringStringMap(ois); 387d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return entry; 388d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 389d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 390d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 391d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Creates a cache entry for the specified data. 392d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 393d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public Entry toCacheEntry(byte[] data) { 394d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Entry e = new Entry(); 395d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.data = data; 396d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.etag = etag; 397d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.serverDate = serverDate; 398d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.ttl = ttl; 399d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru e.softTtl = softTtl; 400e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru e.responseHeaders = responseHeaders; 401d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return e; 402d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 403d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 404d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 405d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Writes the contents of this CacheHeader to the specified OutputStream. 406d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 407d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public boolean writeHeader(OutputStream os) { 408d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 409d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru ObjectOutputStream oos = new ObjectOutputStream(os); 410d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.writeByte(CACHE_VERSION); 411d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.writeUTF(key); 412d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.writeUTF(etag == null ? "" : etag); 413d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.writeLong(serverDate); 414d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.writeLong(ttl); 415d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.writeLong(softTtl); 416e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru writeStringStringMap(responseHeaders, oos); 417d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru oos.flush(); 418d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return true; 419d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (IOException e) { 420d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.d("%s", e.toString()); 421d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return false; 422d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 423d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 424e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 425e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru /** 426e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru * Writes all entries of {@code map} into {@code oos}. 427e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru */ 428e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru private static void writeStringStringMap(Map<String, String> map, ObjectOutputStream oos) 429e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru throws IOException { 430e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru if (map != null) { 431e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru oos.writeInt(map.size()); 432e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru for (Map.Entry<String, String> entry : map.entrySet()) { 433e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru oos.writeUTF(entry.getKey()); 434e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru oos.writeUTF(entry.getValue()); 435e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 436e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } else { 437e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru oos.writeInt(0); 438e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 439e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 440e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 441e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru /** 442e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru * @return a string to string map which contains the entries read from {@code ois} 443e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru * previously written by {@link #writeStringStringMap} 444e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru */ 445e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru private static Map<String, String> readStringStringMap(ObjectInputStream ois) 446e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru throws IOException { 447e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru int size = ois.readInt(); 448e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru Map<String, String> result = (size == 0) 449e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru ? Collections.<String, String>emptyMap() 450e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru : new HashMap<String, String>(size); 451e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru for (int i = 0; i < size; i++) { 452e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru String key = ois.readUTF(); 453e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru String value = ois.readUTF(); 454e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru result.put(key, value); 455e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 456e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru return result; 457e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru } 458d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 459d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 460d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static class CountingInputStream extends FilterInputStream { 461d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private int bytesRead = 0; 462d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 463d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private CountingInputStream(InputStream in) { 464d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru super(in); 465d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 466d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 467d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 468d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public int read() throws IOException { 469d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int result = super.read(); 470d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (result != -1) { 471d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru bytesRead++; 472d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 473d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return result; 474d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 475d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 476d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 477d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public int read(byte[] buffer, int offset, int count) throws IOException { 478d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int result = super.read(buffer, offset, count); 479d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (result != -1) { 480d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru bytesRead += result; 481d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 482d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return result; 483d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 484d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 485d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru} 486