17 package android.util;
18
19 import java.util.LinkedHashMap;
20 import java.util.Map;
21
22 /**
23 * A cache that holds strong references to a limited number of values. Each time
24 * a value is accessed, it is moved to the head of a queue. When a value is
25 * added to a full cache, the value at the end of that queue is evicted and may
26 * become eligible for garbage collection.
一个cache缓冲区,维护有限个值的强引用。当一个值被命中时,它被移到队列的头部???确定有移动么
当一个值被加入一个已满的cache中时,队列最后一个值被舍弃。可能会被垃圾回收
28 * <p>If your cached values hold resources that need to be explicitly released,
29 * override {@link #entryRemoved}.
如果你缓存的值中有必须被释放的资源,需要override entryRemoved方法
31 * <p>If a cache miss should be computed on demand for the corresponding keys,
32 * override {@link #create}. This simplifies the calling code, allowing it to
33 * assume a value will always be returned, even when there's a cache miss.
如果一次cache未命中时,同样需要返回。 需要override create()方法。
这使调用简化:不管是不是命中,都返回一个值。
因为当前的create方法返回的是个null。当你没get到的时候,会调用create方法。在当前情况下,返回null会直接return null。
不会往后执行之后的创建键值对,并返回的操作。
但是如果你重写了create(),使其返回的不是null,则后边的操作会执行。调用get(key),value值被返回。
35 * <p>By default, the cache size is measured in the number of entries. Override
36 * {@link #sizeOf} to size the cache in different units. For example, this cache
37 * is limited to 4MiB of bitmaps:
默认情况下,一个cache的大小,是以实体的个数来衡量的。因为当前的sizeOf() 方法返回的是1,也就是每个entry大小是1
你可以override sizeof方法,改变cache 的最小单元大小。
例如,下边这个cache 限制为最大4MiB的bitmap cache
每个单元的大小就不是1了,而是bitmap的大小。
38 * <pre> {@code
39 * int cacheSize = 4 * 1024 * 1024; // 4MiB
40 * LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
41 * protected int sizeOf(String key, Bitmap value) {
42 * return value.getByteCount();
43 * }
44 * }}</pre>
LRUCache类是线程安全的。多个cache操作时候。要synchronize这个cache
46 * <p>This class is thread-safe. Perform multiple cache operations atomically by
47 * synchronizing on the cache: <pre> {@code
48 * synchronized (cache) {
49 * if (cache.get(key) == null) {
50 * cache.put(key, value);
51 * }
52 * }}</pre>
这个类不允许传入的key和value是空。
get put remove返回null,代表这个key不再cache中
54 * <p>This class does not allow null to be used as a key or value. A return
55 * value of null from {@link #get}, {@link #put} or {@link #remove} is
56 * unambiguous: the key was not in the cache.
57 *
58 * <p>This class appeared in Android 3.1 (Honeycomb MR1); it's available as part
59 * of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
60 * Support Package</a> for earlier releases.
61 */
62 public class LruCache<K, V> {
63 private final LinkedHashMap<K, V> map;
64
65 /** Size of this cache in units. Not necessarily the number of elements. */
66 private int size;
67 private int maxSize;
68
69 private int putCount;
70 private int createCount;
71 private int evictionCount;
72 private int hitCount;
73 private int missCount;
74
75 /**
76 * @param maxSize for caches that do not override {@link #sizeOf}, this is
77 * the maximum number of entries in the cache. For all other caches,
78 * this is the maximum sum of the sizes of the entries in this cache.
79 */
80 public LruCache(int maxSize) {
81 if (maxSize <= 0) {
82 throw new IllegalArgumentException("maxSize <= 0");
83 }
84 this.maxSize = maxSize;
85 this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
86 }
87
88 /**
89 * Sets the size of the cache.
90 * @param maxSize The new maximum size.
91 *
92 * @hide
93 */
94 public void resize(int maxSize) {
95 if (maxSize <= 0) {
96 throw new IllegalArgumentException("maxSize <= 0");
97 }
98
99 synchronized (this) {
100 this.maxSize = maxSize;
101 }
102 trimToSize(maxSize);
103 }
104
105 /**
106 * Returns the value for {@code key} if it exists in the cache or can be
107 * created by {@code #create}. If a value was returned, it is moved to the
108 * head of the queue. This returns null if a value is not cached and cannot
109 * be created.
110 */
111 public final V get(K key) {
112 if (key == null) {
113 throw new NullPointerException("key == null");
114 }
115
116 V mapValue;
117 synchronized (this) {
118 mapValue = map.get(key);
119 if (mapValue != null) {
120 hitCount++;
121 return mapValue;
122 }
123 missCount++;
124 }
125
126 /*
127 * Attempt to create a value. This may take a long time, and the map
128 * may be different when create() returns. If a conflicting value was
129 * added to the map while create() was working, we leave that value in
130 * the map and release the created value.
131 */
132
133 V createdValue = create(key);
134 if (createdValue == null) {
135 return null;
136 }
137
138 synchronized (this) {
139 createCount++;
140 mapValue = map.put(key, createdValue);
141
142 if (mapValue != null) {
143 // There was a conflict so undo that last put
144 map.put(key, mapValue);
145 } else {
146 size += safeSizeOf(key, createdValue);
147 }
148 }
149
150 if (mapValue != null) {
151 entryRemoved(false, key, createdValue, mapValue);
152 return mapValue;
153 } else {
154 trimToSize(maxSize);
155 return createdValue;
156 }
157 }
158
159 /**
160 * Caches {@code value} for {@code key}. The value is moved to the head of
161 * the queue.
162 *
163 * @return the previous value mapped by {@code key}.
164 */
165 public final V put(K key, V value) {
166 if (key == null || value == null) {
167 throw new NullPointerException("key == null || value == null");
168 }
169
170 V previous;
171 synchronized (this) {
172 putCount++;
173 size += safeSizeOf(key, value);
174 previous = map.put(key, value);
175 if (previous != null) {
176 size -= safeSizeOf(key, previous);
177 }
178 }
179
180 if (previous != null) {
181 entryRemoved(false, key, previous, value);
182 }
183
184 trimToSize(maxSize);
185 return previous;
186 }
187
188 /**
189 * Remove the eldest entries until the total of remaining entries is at or
190 * below the requested size.
191 *
192 * @param maxSize the maximum size of the cache before returning. May be -1
193 * to evict even 0-sized elements.
194 */
195 public void trimToSize(int maxSize) {
196 while (true) {
197 K key;
198 V value;
199 synchronized (this) {
200 if (size < 0 || (map.isEmpty() && size != 0)) {
201 throw new IllegalStateException(getClass().getName()
202 + ".sizeOf() is reporting inconsistent results!");
203 }
204
205 if (size <= maxSize) {
206 break;
207 }
208
209 Map.Entry<K, V> toEvict = map.eldest();
210 if (toEvict == null) {
211 break;
212 }
213
214 key = toEvict.getKey();
215 value = toEvict.getValue();
216 map.remove(key);
217 size -= safeSizeOf(key, value);
218 evictionCount++;
219 }
220
221 entryRemoved(true, key, value, null);
222 }
223 }
224
225 /**
226 * Removes the entry for {@code key} if it exists.
227 *
228 * @return the previous value mapped by {@code key}.
229 */
230 public final V remove(K key) {
231 if (key == null) {
232 throw new NullPointerException("key == null");
233 }
234
235 V previous;
236 synchronized (this) {
237 previous = map.remove(key);
238 if (previous != null) {
239 size -= safeSizeOf(key, previous);
240 }
241 }
242
243 if (previous != null) {
244 entryRemoved(false, key, previous, null);
245 }
246
247 return previous;
248 }
249
250 /**
251 * Called for entries that have been evicted or removed. This method is
252 * invoked when a value is evicted to make space, removed by a call to
253 * {@link #remove}, or replaced by a call to {@link #put}. The default
254 * implementation does nothing.
255 *
256 * <p>The method is called without synchronization: other threads may
257 * access the cache while this method is executing.
258 *
259 * @param evicted true if the entry is being removed to make space, false
260 * if the removal was caused by a {@link #put} or {@link #remove}.
261 * @param newValue the new value for {@code key}, if it exists. If non-null,
262 * this removal was caused by a {@link #put}. Otherwise it was caused by
263 * an eviction or a {@link #remove}.
264 */
265 protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
266
267 /**
268 * Called after a cache miss to compute a value for the corresponding key.
269 * Returns the computed value or null if no value can be computed. The
270 * default implementation returns null.
271 *
272 * <p>The method is called without synchronization: other threads may
273 * access the cache while this method is executing.
274 *
275 * <p>If a value for {@code key} exists in the cache when this method
276 * returns, the created value will be released with {@link #entryRemoved}
277 * and discarded. This can occur when multiple threads request the same key
278 * at the same time (causing multiple values to be created), or when one
279 * thread calls {@link #put} while another is creating a value for the same
280 * key.
281 */
282 protected V create(K key) {
283 return null;
284 }
285
286 private int safeSizeOf(K key, V value) {
287 int result = sizeOf(key, value);
288 if (result < 0) {
289 throw new IllegalStateException("Negative size: " + key + "=" + value);
290 }
291 return result;
292 }
293
294 /**
295 * Returns the size of the entry for {@code key} and {@code value} in
296 * user-defined units. The default implementation returns 1 so that size
297 * is the number of entries and max size is the maximum number of entries.
298 *
299 * <p>An entry's size must not change while it is in the cache.
300 */
301 protected int sizeOf(K key, V value) {
302 return 1;
303 }
304
305 /**
306 * Clear the cache, calling {@link #entryRemoved} on each removed entry.
307 */
308 public final void evictAll() {
309 trimToSize(-1); // -1 will evict 0-sized elements
310 }
311
312 /**
313 * For caches that do not override {@link #sizeOf}, this returns the number
314 * of entries in the cache. For all other caches, this returns the sum of
315 * the sizes of the entries in this cache.
316 */
317 public synchronized final int size() {
318 return size;
319 }
320
321 /**
322 * For caches that do not override {@link #sizeOf}, this returns the maximum
323 * number of entries in the cache. For all other caches, this returns the
324 * maximum sum of the sizes of the entries in this cache.
325 */
326 public synchronized final int maxSize() {
327 return maxSize;
328 }
329
330 /**
331 * Returns the number of times {@link #get} returned a value that was
332 * already present in the cache.
333 */
334 public synchronized final int hitCount() {
335 return hitCount;
336 }
337
338 /**
339 * Returns the number of times {@link #get} returned null or required a new
340 * value to be created.
341 */
342 public synchronized final int missCount() {
343 return missCount;
344 }
345
346 /**
347 * Returns the number of times {@link #create(Object)} returned a value.
348 */
349 public synchronized final int createCount() {
350 return createCount;
351 }
352
353 /**
354 * Returns the number of times {@link #put} was called.
355 */
356 public synchronized final int putCount() {
357 return putCount;
358 }
359
360 /**
361 * Returns the number of values that have been evicted.
362 */
363 public synchronized final int evictionCount() {
364 return evictionCount;
365 }
366
367 /**
368 * Returns a copy of the current contents of the cache, ordered from least
369 * recently accessed to most recently accessed.
370 */
371 public synchronized final Map<K, V> snapshot() {
372 return new LinkedHashMap<K, V>(map);
373 }
374
375 @Override public synchronized final String toString() {
376 int accesses = hitCount + missCount;
377 int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
378 return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
379 maxSize, hitCount, missCount, hitPercent);
380 }
381 }