LruCache.java这个类用来作为缓存图片是很不错的,源码的分析:

来源:互联网 发布:淘宝加盟被骗了有合同 编辑:程序博客网 时间:2024/05/18 02:03
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
/**
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
packageandroid.util;
 
importjava.util.LinkedHashMap;
importjava.util.Map;
 
/***
 * A cache that holds strong references to a limited number of values. Each time
 * a value is accessed, it is moved to the head of a queue. When a value is
 * added to a full cache, the value at the end of that queue is evicted and may
 * become eligible for garbage collection.
 *
 * <p>If your cached values hold resources that need to be explicitly released,
 * override {<a href="\"http://www.eoeandroid.com/home.php?mod=space&uid=91636\"" target="\"_blank\"">@link</a> #entryRemoved}.
 *
 * <p>If a cache miss should be computed on demand for the corresponding keys,
 * override {@link #create}. This simplifies the calling code, allowing it to
 * assume a value will always be returned, even when there's a cache miss.
 *
 * <p>By default, the cache size is measured in the number of entries. Override
 * {@link #sizeOf} to size the cache in different units. For example, this cache
 * is limited to 4MiB of bitmaps:
 * <pre>   {@code
 *   int cacheSize = 4 * 1024 * 1024; // 4MiB
 *   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
 *       protected int sizeOf(String key, Bitmap value) {
 *           return value.getByteCount();
 *       }
 *   }}</pre>
 *
 * <p>This class is thread-safe. Perform multiple cache operations atomically by
 * synchronizing on the cache: <pre>   {@code
 *   synchronized (cache) {
 *     if (cache.get(key) == null) {
 *         cache.put(key, value);
 *     }
 *   }}</pre>
 *
 * <p>This class does not allow null to be used as a key or value. A return
 * value of null from {@link #get}, {@link #put} or {@link #remove} is
 * unambiguous: the key was not in the cache.
 */
publicclass LruCache<K, V> {
   privatefinal LinkedHashMap<K, V> map;
 
   /*** Size of this cache in units. Not necessarily the number of elements. */
   privateint size;//已经存储的大小,缓存中所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存
   privateint maxSize;//缓存只能占用的最大堆内存,这个在构造函数里面就需要传参数确定大小的
 
   privateint putCount;//put的次数
   privateint createCount;//create的次数
   privateint evictionCount;//回收的次数
   privateint hitCount;//命中的次数
   privateint missCount;//丢失的次数
 
   /***
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *     the maximum number of entries in the cache. For all other caches,
     *     this is the maximum sum of the sizes of the entries in this cache.
     */
   publicLruCache(intmaxSize) {
        if(maxSize <= 0) {
            thrownew IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
         
        //LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU
        //这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率
        this.map = newLinkedHashMap<K, V>(0,0.75f,true);
   }
 
   /***
     * 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
     * created by {@code #create}. If a value was returned, it is moved to the
     * head of the queue. This returns null if a value is not cached and cannot
     * be created.
     */
   publicfinal V get(K key) {
        if(key == null) {
            thrownew NullPointerException("key == null");
        }
 
        V mapValue;
        synchronized(this) {
            mapValue = map.get(key);
            if(mapValue != null) {
                hitCount++;
                returnmapValue;
            }
            missCount++;
        }
 
        /**
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */
 
        V createdValue = create(key);//这个默认都是返回为null,需要我们自己去实现它
        if(createdValue == null) {
            returnnull;
        }
 
        /**
         * LinkedHashMapMap不支持线程同步。
         * 这里为了防止任一时刻可以有多个线程同时写LinkedHashMapMap可能会导致数据的不一致所以使用了同步机制
         */
        synchronized(this) {
            createCount++;
            //如果当前map中没有key值则返回的是null,如果已经存在相同的key值则返回的值是之前该key所保存的value值
            mapValue = map.put(key, createdValue);
 
            if(mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            }else{//计算当前的容量
                size += safeSizeOf(key, createdValue);
            }
        }
 
        if(mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            returnmapValue;
        }else{
            trimToSize(maxSize);
            returncreatedValue;
        }
   }
 
   /***
     * Caches {@code value} for {@code key}. The value is moved to the head of
     * the queue.
     *
     * <a href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\"" target="\"_blank\"">@return</a> the previous value mapped by {@code key}.
     */
   publicfinal V put(K key, V value) {
        if(key == null|| value == null) {
            thrownew NullPointerException("key == null || value == null");
        }
 
        V previous;
        synchronized(this) {
            putCount++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if(previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }
 
        if(previous != null) {
            entryRemoved(false, key, previous, value);
        }
 
        trimToSize(maxSize);
        returnprevious;
   }
 
   /***
     * @param maxSize the maximum size of the cache before returning. May be -1
     *     to evict even 0-sized elements.
     *     这个方法主要是比较当前的存储容量和总容量的大小即 size和maxSize,
     *     如果当前容量大于总容量的大小就比较将最近最少使用的元素来替换以提高效率
     *     如果知道是最近最少主要是在于
     *     LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列
     */
   privatevoid trimToSize(intmaxSize) {
        while(true) {
            K key;
            V value;
            synchronized(this) {
                if(size < 0|| (map.isEmpty() && size != 0)) {
                    thrownew IllegalStateException(getClass().getName()
                            +".sizeOf() is reporting inconsistent results!");
                }
 
                if(size <= maxSize) {
                    break;
                }
                 
                Map.Entry<K, V> toEvict = map.eldest();//这个eldest()方法可以理解为取出map最前面的那个值
                if(toEvict == null) {
                    break;
                }
 
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);//这里是移除改值之后要把该值所占用的存储空间去掉
                evictionCount++;
            }
 
            //这个方法不会默认不会做任何处理,如果有需要我们可以去实现该方法去做我们想做的事
            entryRemoved(true, key, value, null);
        }
   }
 
   /***
     * Removes the entry for {@code key} if it exists.
     *
     * @return the previous value mapped by {@code key}.
     * 移除指定key的value值
     */
   publicfinal V remove(K key) {
        if(key == null) {
            thrownew NullPointerException("key == null");
        }
 
        V previous;
        synchronized(this) {
            previous = map.remove(key);//这个是返回该key的value值
            if(previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }
 
        if(previous != null) {
            entryRemoved(false, key, previous, null);
        }
 
        returnprevious;
   }
 
   /***
     * Called for entries that have been evicted or removed. This method is
     * invoked when a value is evicted to make space, removed by a call to
     * {@link #remove}, or replaced by a call to {@link #put}. The default
     * implementation does nothing.
     *
     * <p>The method is called without synchronization: other threads may
     * access the cache while this method is executing.
     *
     * @param evicted true if the entry is being removed to make space, false
     *     if the removal was caused by a {@link #put} or {@link #remove}.
     * @param newValue the new value for {@code key}, if it exists. If non-null,
     *     this removal was caused by a {@link #put}. Otherwise it was caused by
     *     an eviction or a {@link #remove}.
     *     在移除某一个值时会调用这个方法,包括容量不够系统主动移除以及插入重复的key时要移除该key的上一个value值
     *     这个方法默认不做任何处理,我们可以自己去实现该方法做我们想做的事情
     */
   protectedvoid entryRemoved(booleanevicted, K key, V oldValue, V newValue) {}
 
   /***
     * Called after a cache miss to compute a value for the corresponding key.
     * Returns the computed value or null if no value can be computed. The
     * default implementation returns null.
     *
     * <p>The method is called without synchronization: other threads may
     * access the cache while this method is executing.
     *
     * <p>If a value for {@code key} exists in the cache when this method
     * returns, the created value will be released with {@link #entryRemoved}
     * and discarded. This can occur when multiple threads request the same key
     * at the same time (causing multiple values to be created), or when one
     * thread calls {@link #put} while another is creating a value for the same
     * key.
     * 这个方法试在get的时候如果没有该key值时执行的方法,这个默认是直接返回null
     * 我们可以自行重写该方法做我们想要做的处理
     */
   protectedV create(K key) {
        returnnull;
   }
 
   /**
     * 计算当前key值所对应的value的存储容量
     */
   privateint safeSizeOf(K key, V value) {
        intresult = sizeOf(key, value);
        if(result < 0) {
            thrownew IllegalStateException("Negative size: " + key + "="+ value);
        }
        returnresult;
   }
 
   /***
     * Returns the size of the entry for {@code key} and {@code value} in
     * user-defined units.  The default implementation returns 1 so that size
     * is the number of entries and max size is the maximum number of entries.
     *
     * <p>An entry's size must not change while it is in the cache.
     * 这个safeSizeOf()方法调用来计算该key值所对应的存储容量
     * 默认是方法回1,但是在应用中我们一般都要去重写改方法来返回实际value的大小,列如下面的代码就是计算Bitmap的大小
     * 计算Bitmap内存占用也可以是bitmap.getRowBytes() * bitmap.getHeight();
     *  LruCache<String,Bitmap> mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
     *   @Override
     *   protected int sizeOf(String key, Bitmap bitmap) {
     *       return bitmap.getByteCount() / 1024;
     *   }
     *  };
     */
   protectedint sizeOf(K key, V value) {
        return1;
   }
 
   /***
     * Clear the cache, calling {@link #entryRemoved} on each removed entry.
     * 这个一看就是清空cacke
     */
   publicfinal void evictAll() {
        trimToSize(-1);// -1 will evict 0-sized elements
   }
 
   /***
     * For caches that do not override {@link #sizeOf}, this returns the number
     * of entries in the cache. For all other caches, this returns the sum of
     * the sizes of the entries in this cache.
     * 当前已经已经存储容量的大小
     */
   publicsynchronized final int size() {
        returnsize;
   }
 
   /***
     * For caches that do not override {@link #sizeOf}, this returns the maximum
     * number of entries in the cache. For all other caches, this returns the
     * maximum sum of the sizes of the entries in this cache.
     * 总容量的大小
     */
   publicsynchronized final int maxSize() {
        returnmaxSize;
   }
 
   /***
     * Returns the number of times {@link #get} returned a value that was
     * already present in the cache.
     */
   publicsynchronized final int hitCount() {
        returnhitCount;
   }
 
   /***
     * Returns the number of times {@link #get} returned null or required a new
     * value to be created.
     */
   publicsynchronized final int missCount() {
        returnmissCount;
   }
 
   /***
     * Returns the number of times {@link #create(Object)} returned a value.
     */
   publicsynchronized final int createCount() {
        returncreateCount;
   }
 
   /***
     * Returns the number of times {@link #put} was called.
     */
   publicsynchronized final int putCount() {
        returnputCount;
   }
 
   /***
     * Returns the number of values that have been evicted.
     */
   publicsynchronized final int evictionCount() {
        returnevictionCount;
   }
 
   /***
     * Returns a copy of the current contents of the cache, ordered from least
     * recently accessed to most recently accessed.
     * 返回当前cache的副本,从最近最少访问到最多访问
     */
   publicsynchronized final Map<K, V> snapshot() {
        returnnew LinkedHashMap<K, V>(map);
   }
 
     /**
      * 这个主要是返回内存使用的情况
      */
   @Overridepublic synchronized final String toString() {
        intaccesses = hitCount + missCount;
        inthitPercent = accesses != 0? (100* hitCount / accesses) : 0;
        returnString.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
                maxSize, hitCount, missCount, hitPercent);
   }
}