LruCache.java这个类用来作为缓存图片是很不错的,下面是我对源码的分析:
转自:
http://www.eoeandroid.com/thread-286816-1-1.html
001 | /** |
002 | * Copyright (C) 2011 The Android Open Source Project |
003 | * |
004 | * Licensed under the Apache License, Version 2.0 (the "License"); |
005 | * you may not use this file except in compliance with the License. |
006 | * You may obtain a copy of the License at |
007 | * |
009 | * |
010 | * Unless required by applicable law or agreed to in writing, software |
011 | * distributed under the License is distributed on an "AS IS" BASIS, |
012 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
013 | * See the License for the specific language governing permissions and |
014 | * limitations under the License. |
015 | */ |
016 | |
017 | package android.util; |
018 | |
019 | import java.util.LinkedHashMap; |
020 | import java.util.Map; |
021 | |
022 | /*** |
023 | * A cache that holds strong references to a limited number of values. Each time |
024 | * a value is accessed, it is moved to the head of a queue. When a value is |
025 | * added to a full cache, the value at the end of that queue is evicted and may |
026 | * become eligible for garbage collection. |
027 | * |
028 | * <p>If your cached values hold resources that need to be explicitly released, |
029 | * override {<A href='\"http://www.eoeandroid.com/home.php?mod=space&uid=91636\"' target='\"_blank\"'>@link</A> #entryRemoved}. |
030 | * |
031 | * <p>If a cache miss should be computed on demand for the corresponding keys, |
032 | * override {@link #create}. This simplifies the calling code, allowing it to |
033 | * assume a value will always be returned, even when there's a cache miss. |
034 | * |
035 | * <p>By default, the cache size is measured in the number of entries. Override |
036 | * {@link #sizeOf} to size the cache in different units. For example, this cache |
037 | * is limited to 4MiB of bitmaps: |
038 | * <pre> {@code |
039 | * int cacheSize = 4 * 1024 * 1024; // 4MiB |
040 | * LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { |
041 | * protected int sizeOf(String key, Bitmap value) { |
042 | * return value.getByteCount(); |
043 | * } |
044 | * }}</pre> |
045 | * |
046 | * <p>This class is thread-safe. Perform multiple cache operations atomically by |
047 | * synchronizing on the cache: <pre> {@code |
048 | * synchronized (cache) { |
049 | * if (cache.get(key) == null) { |
050 | * cache.put(key, value); |
051 | * } |
052 | * }}</pre> |
053 | * |
054 | * <p>This class does not allow null to be used as a key or value. A return |
055 | * value of null from {@link #get}, {@link #put} or {@link #remove} is |
056 | * unambiguous: the key was not in the cache. |
057 | */ |
058 | public class LruCache<K, V> { |
059 | private final LinkedHashMap<K, V> map; |
060 | |
061 | /*** Size of this cache in units. Not necessarily the number of elements. */ |
062 | private int size; //已经存储的大小,缓存中所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存 |
063 | private int maxSize; //缓存只能占用的最大堆内存,这个在构造函数里面就需要传参数确定大小的 |
064 | |
065 | private int putCount; //put的次数 |
066 | private int createCount; //create的次数 |
067 | private int evictionCount; //回收的次数 |
068 | private int hitCount; //命中的次数 |
069 | private int missCount; //丢失的次数 |
070 | |
071 | /*** |
072 | * @param maxSize for caches that do not override {@link #sizeOf}, this is |
073 | * the maximum number of entries in the cache. For all other caches, |
074 | * this is the maximum sum of the sizes of the entries in this cache. |
075 | */ |
076 | public LruCache( int maxSize) { |
077 | if (maxSize <= 0 ) { |
078 | throw new IllegalArgumentException( "maxSize <= 0" ); |
079 | } |
080 | this .maxSize = maxSize; |
081 | |
082 | //LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU |
083 | //这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率 |
084 | this .map = new LinkedHashMap<K, V>( 0 , 0 .75f, true ); |
085 | } |
086 | |
087 | /*** |
088 | * Returns the value for {<A href='\"http://www.eoeandroid.com/home.php?mod=space&uid=38354\"' target='\"_blank\"'>@Code</A> key} if it exists in the cache or can be |
089 | * created by {@code #create}. If a value was returned, it is moved to the |
090 | * head of the queue. This returns null if a value is not cached and cannot |
091 | * be created. |
092 | */ |
093 | public final V get(K key) { |
094 | if (key == null ) { |
095 | throw new NullPointerException( "key == null" ); |
096 | } |
097 | |
098 | V mapValue; |
099 | synchronized ( this ) { |
100 | mapValue = map.get(key); |
101 | if (mapValue != null ) { |
102 | hitCount++; |
103 | return mapValue; |
104 | } |
105 | missCount++; |
106 | } |
107 | |
108 | /** |
109 | * Attempt to create a value. This may take a long time, and the map |
110 | * may be different when create() returns. If a conflicting value was |
111 | * added to the map while create() was working, we leave that value in |
112 | * the map and release the created value. |
113 | */ |
114 | |
115 | V createdValue = create(key); //这个默认都是返回为null,需要我们自己去实现它 |
116 | if (createdValue == null ) { |
117 | return null ; |
118 | } |
119 | |
120 | /** |
121 | * LinkedHashMapMap不支持线程同步。 |
122 | * 这里为了防止任一时刻可以有多个线程同时写LinkedHashMapMap可能会导致数据的不一致所以使用了同步机制 |
123 | */ |
124 | synchronized ( this ) { |
125 | createCount++; |
126 | //如果当前map中没有key值则返回的是null,如果已经存在相同的key值则返回的值是之前该key所保存的value值 |
127 | mapValue = map.put(key, createdValue); |
128 | |
129 | if (mapValue != null ) { |
130 | // There was a conflict so undo that last put |
131 | map.put(key, mapValue); |
132 | } else { //计算当前的容量 |
133 | size += safeSizeOf(key, createdValue); |
134 | } |
135 | } |
136 | |
137 | if (mapValue != null ) { |
138 | entryRemoved( false , key, createdValue, mapValue); |
139 | return mapValue; |
140 | } else { |
141 | trimToSize(maxSize); |
142 | return createdValue; |
143 | } |
144 | } |
145 | |
146 | /*** |
147 | * Caches {@code value} for {@code key}. The value is moved to the head of |
148 | * the queue. |
149 | * |
150 | * <A href='\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\"' target='\"_blank\"'>@return</A> the previous value mapped by {@code key}. |
151 | */ |
152 | public final V put(K key, V value) { |
153 | if (key == null || value == null ) { |
154 | throw new NullPointerException( "key == null || value == null" ); |
155 | } |
156 | |
157 | V previous; |
158 | synchronized ( this ) { |
159 | putCount++; |
160 | size += safeSizeOf(key, value); |
161 | previous = map.put(key, value); |
162 | if (previous != null ) { |
163 | size -= safeSizeOf(key, previous); |
164 | } |
165 | } |
166 | |
167 | if (previous != null ) { |
168 | entryRemoved( false , key, previous, value); |
169 | } |
170 | |
171 | trimToSize(maxSize); |
172 | return previous; |
173 | } |
174 | |
175 | /*** |
176 | * @param maxSize the maximum size of the cache before returning. May be -1 |
177 | * to evict even 0-sized elements. |
178 | * 这个方法主要是比较当前的存储容量和总容量的大小即 size和maxSize, |
179 | * 如果当前容量大于总容量的大小就比较将最近最少使用的元素来替换以提高效率 |
180 | * 如果知道是最近最少主要是在于 |
181 | * LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列 |
182 | */ |
183 | private void trimToSize( int maxSize) { |
184 | while ( true ) { |
185 | K key; |
186 | V value; |
187 | synchronized ( this ) { |
188 | if (size < 0 || (map.isEmpty() && size != 0 )) { |
189 | throw new IllegalStateException(getClass().getName() |
190 | + ".sizeOf() is reporting inconsistent results!" ); |
191 | } |
192 | |
193 | if (size <= maxSize) { |
194 | break ; |
195 | } |
196 | |
197 | Map.Entry<K, V> toEvict = map.eldest(); //这个eldest()方法可以理解为取出map最前面的那个值 |
198 | if (toEvict == null ) { |
199 | break ; |
200 | } |
201 | |
202 | key = toEvict.getKey(); |
203 | value = toEvict.getValue(); |
204 | map.remove(key); |
205 | size -= safeSizeOf(key, value); //这里是移除改值之后要把该值所占用的存储空间去掉 |
206 | evictionCount++; |
207 | } |
208 | |
209 | //这个方法不会默认不会做任何处理,如果有需要我们可以去实现该方法去做我们想做的事 |
210 | entryRemoved( true , key, value, null ); |
211 | } |
212 | } |
213 | |
214 | /*** |
215 | * Removes the entry for {@code key} if it exists. |
216 | * |
217 | * @return the previous value mapped by {@code key}. |
218 | * 移除指定key的value值 |
219 | */ |
220 | public final V remove(K key) { |
221 | if (key == null ) { |
222 | throw new NullPointerException( "key == null" ); |
223 | } |
224 | |
225 | V previous; |
226 | synchronized ( this ) { |
227 | previous = map.remove(key); //这个是返回该key的value值 |
228 | if (previous != null ) { |
229 | size -= safeSizeOf(key, previous); |
230 | } |
231 | } |
232 | |
233 | if (previous != null ) { |
234 | entryRemoved( false , key, previous, null ); |
235 | } |
236 | |
237 | return previous; |
238 | } |
239 | |
240 | /*** |
241 | * Called for entries that have been evicted or removed. This method is |
242 | * invoked when a value is evicted to make space, removed by a call to |
243 | * {@link #remove}, or replaced by a call to {@link #put}. The default |
244 | * implementation does nothing. |
245 | * |
246 | * <p>The method is called without synchronization: other threads may |
247 | * access the cache while this method is executing. |
248 | * |
249 | * @param evicted true if the entry is being removed to make space, false |
250 | * if the removal was caused by a {@link #put} or {@link #remove}. |
251 | * @param newValue the new value for {@code key}, if it exists. If non-null, |
252 | * this removal was caused by a {@link #put}. Otherwise it was caused by |
253 | * an eviction or a {@link #remove}. |
254 | * 在移除某一个值时会调用这个方法,包括容量不够系统主动移除以及插入重复的key时要移除该key的上一个value值 |
255 | * 这个方法默认不做任何处理,我们可以自己去实现该方法做我们想做的事情 |
256 | */ |
257 | protected void entryRemoved( boolean evicted, K key, V oldValue, V newValue) {} |
258 | |
259 | /*** |
260 | * Called after a cache miss to compute a value for the corresponding key. |
261 | * Returns the computed value or null if no value can be computed. The |
262 | * default implementation returns null. |
263 | * |
264 | * <p>The method is called without synchronization: other threads may |
265 | * access the cache while this method is executing. |
266 | * |
267 | * <p>If a value for {@code key} exists in the cache when this method |
268 | * returns, the created value will be released with {@link #entryRemoved} |
269 | * and discarded. This can occur when multiple threads request the same key |
270 | * at the same time (causing multiple values to be created), or when one |
271 | * thread calls {@link #put} while another is creating a value for the same |
272 | * key. |
273 | * 这个方法试在get的时候如果没有该key值时执行的方法,这个默认是直接返回null |
274 | * 我们可以自行重写该方法做我们想要做的处理 |
275 | */ |
276 | protected V create(K key) { |
277 | return null ; |
278 | } |
279 | |
280 | /** |
281 | * 计算当前key值所对应的value的存储容量 |
282 | */ |
283 | private int safeSizeOf(K key, V value) { |
284 | int result = sizeOf(key, value); |
285 | if (result < 0 ) { |
286 | throw new IllegalStateException( "Negative size: " + key + "=" + value); |
287 | } |
288 | return result; |
289 | } |
290 | |
291 | /*** |
292 | * Returns the size of the entry for {@code key} and {@code value} in |
293 | * user-defined units. The default implementation returns 1 so that size |
294 | * is the number of entries and max size is the maximum number of entries. |
295 | * |
296 | * <p>An entry's size must not change while it is in the cache. |
297 | * 这个safeSizeOf()方法调用来计算该key值所对应的存储容量 |
298 | * 默认是方法回1,但是在应用中我们一般都要去重写改方法来返回实际value的大小,列如下面的代码就是计算Bitmap的大小 |
299 | * 计算Bitmap内存占用也可以是bitmap.getRowBytes() * bitmap.getHeight(); |
300 | * LruCache<String,Bitmap> mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { |
301 | * @Override |
302 | * protected int sizeOf(String key, Bitmap bitmap) { |
303 | * return bitmap.getByteCount() / 1024; |
304 | * } |
305 | * }; |
306 | */ |
307 | protected int sizeOf(K key, V value) { |
308 | return 1 ; |
309 | } |
310 | |
311 | /*** |
312 | * Clear the cache, calling {@link #entryRemoved} on each removed entry. |
313 | * 这个一看就是清空cacke |
314 | */ |
315 | public final void evictAll() { |
316 | trimToSize(- 1 ); // -1 will evict 0-sized elements |
317 | } |
318 | |
319 | /*** |
320 | * For caches that do not override {@link #sizeOf}, this returns the number |
321 | * of entries in the cache. For all other caches, this returns the sum of |
322 | * the sizes of the entries in this cache. |
323 | * 当前已经已经存储容量的大小 |
324 | */ |
325 | public synchronized final int size() { |
326 | return size; |
327 | } |
328 | |
329 | /*** |
330 | * For caches that do not override {@link #sizeOf}, this returns the maximum |
331 | * number of entries in the cache. For all other caches, this returns the |
332 | * maximum sum of the sizes of the entries in this cache. |
333 | * 总容量的大小 |
334 | */ |
335 | public synchronized final int maxSize() { |
336 | return maxSize; |
337 | } |
338 | |
339 | /*** |
340 | * Returns the number of times {@link #get} returned a value that was |
341 | * already present in the cache. |
342 | */ |
343 | public synchronized final int hitCount() { |
344 | return hitCount; |
345 | } |
346 | |
347 | /*** |
348 | * Returns the number of times {@link #get} returned null or required a new |
349 | * value to be created. |
350 | */ |
351 | public synchronized final int missCount() { |
352 | return missCount; |
353 | } |
354 | |
355 | /*** |
356 | * Returns the number of times {@link #create(Object)} returned a value. |
357 | */ |
358 | public synchronized final int createCount() { |
359 | return createCount; |
360 | } |
361 | |
362 | /*** |
363 | * Returns the number of times {@link #put} was called. |
364 | */ |
365 | public synchronized final int putCount() { |
366 | return putCount; |
367 | } |
368 | |
369 | /*** |
370 | * Returns the number of values that have been evicted. |
371 | */ |
372 | public synchronized final int evictionCount() { |
373 | return evictionCount; |
374 | } |
375 | |
376 | /*** |
377 | * Returns a copy of the current contents of the cache, ordered from least |
378 | * recently accessed to most recently accessed. |
379 | * 返回当前cache的副本,从最近最少访问到最多访问 |
380 | */ |
381 | public synchronized final Map<K, V> snapshot() { |
382 | return new LinkedHashMap<K, V>(map); |
383 | } |
384 | |
385 | /** |
386 | * 这个主要是返回内存使用的情况 |
387 | */ |
388 | @Override public synchronized final String toString() { |
389 | int accesses = hitCount + missCount; |
390 | int hitPercent = accesses != 0 ? ( 100 * hitCount / accesses) : 0 ; |
391 | return String.format( "LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]" , |
392 | maxSize, hitCount, missCount, hitPercent); |
393 | } |
394 | } |