? ? ? ?講到LruCache不得不提一下LinkedHashMap拉盾,因為LruCache中Lru算法的實現(xiàn)就是通過LinkedHashMap來實現(xiàn)的。LinkedHashMap繼承于HashMap涉瘾,它使用了一個雙向鏈表來存儲Map中的Entry順序關(guān)系,這種順序有兩種捷兰,一種是LRU順序找岖,一種是插入順序悔捶,這可以由其構(gòu)造函數(shù)public LinkedHashMap(int initialCapacity,float loadFactor, boolean accessOrder)指定。所以,對于get擎勘、put、remove等操作醉顽,LinkedHashMap除了要做HashMap做的事情并齐,還做些調(diào)整Entry順序鏈表的工作。LruCache中將LinkedHashMap的順序設(shè)置為LRU順序來實現(xiàn)LRU緩存驹沿,每次調(diào)用get(也就是從內(nèi)存緩存中取圖片)艘策,則將該對象移到鏈表的尾端。調(diào)用put插入新的對象也是存儲在鏈表尾端渊季,這樣當(dāng)內(nèi)存緩存達(dá)到設(shè)定的最大值時朋蔫,將鏈表頭部的對象(近期最少用到的)移除。
/*
?*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.
?*/
package android.support.v4.util;
import java.util.LinkedHashMap;
import java.util.Map;
/**
?*Static library version of {@link android.util.LruCache}. Used to write apps
?*that run on API levels prior to 12. When running on API level 12 or above,
?*this implementation is still used; it does not try to switch to the
?*framework's implementation. See the framework SDK documentation for a class
?*overview.
?*/
public class LruCache {
???private final LinkedHashMap map;
???/** Size of this cache in units. Not necessarily the number of elements.*/
???private int size;??? //當(dāng)前cache的大小
???private int maxSize; //cache最大大小
???private int putCount;?????? //put的次數(shù)
???private int createCount;???//create的次數(shù)
???private int evictionCount;? //回收的次數(shù)
???private int hitCount;?????? //命中的次數(shù)
???private int missCount;????? //未命中次數(shù)
???/**
??? ?* @param maxSize for caches that do notoverride {@link #sizeOf}, this is
????*???? the maximum number ofentries in the cache. For all other caches,
????*???? this is the maximum sum ofthe sizes of the entries in this cache.
????*/
???public LruCache(int maxSize) {
???????if (maxSize <= 0) {
???????????throw new IllegalArgumentException("maxSize <= 0");
???????}
???????this.maxSize = maxSize;
???????//將LinkedHashMap的accessOrder設(shè)置為true來實現(xiàn)LRU
???????this.map = new LinkedHashMap(0, 0.75f, true);?
??? }
???/**
????* Returns the value for {@code key} if it exists in the cache or can be
????* created by {@code #create}. If a value was returned, it is moved tothe
????* head of the queue. This returns null if a value is not cached andcannot
????* be created.
????*通過key獲取相應(yīng)的item梭域,或者創(chuàng)建返回相應(yīng)的item斑举。相應(yīng)的item會移動到隊列的尾部,
????*如果item的value沒有被cache或者不能被創(chuàng)建病涨,則返回null富玷。
????*/
???public final V get(K key) {
???????if (key == null) {
???????????throw new NullPointerException("key == null");
???????}
???????V mapValue;
???????synchronized (this) {
???????????mapValue = map.get(key);
???????????if (mapValue != null) {
??????????????? //mapValue不為空表示命中,hitCount+1并返回mapValue對象
??????????????? hitCount++;
??????????????? return mapValue;
???????????}
???????????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.
????????*如果未命中既穆,則試圖創(chuàng)建一個對象赎懦,這里create方法返回null,并沒有實現(xiàn)創(chuàng)建對象的方法
????????*如果需要事項創(chuàng)建對象的方法可以重寫create方法。因為圖片緩存時內(nèi)存緩存沒有命中會去
????????*文件緩存中去取或者從網(wǎng)絡(luò)下載幻工,所以并不需要創(chuàng)建励两。
????????*/
???????V createdValue = create(key);
???????if (createdValue == null) {
???????????return null;
???????}
???????//假如創(chuàng)建了新的對象,則繼續(xù)往下執(zhí)行
???????synchronized (this) {
???????????createCount++;?
???????????//將createdValue加入到map中囊颅,并且將原來鍵為key的對象保存到mapValue
???????????mapValue = map.put(key, createdValue);??
???????????if (mapValue != null) {
??????????????? // There was a conflict so undothat last put
??????????????? //如果mapValue不為空当悔,則撤銷上一步的put操作傅瞻。
??????????????? map.put(key, mapValue);
???????????} else {
??????????????? //加入新創(chuàng)建的對象之后需要重新計算size大小
??????????????? size += safeSizeOf(key,createdValue);
???????????}
???????}
???????if (mapValue != null) {
???????????entryRemoved(false, key, createdValue, mapValue);
???????????return mapValue;
???????} else {
???????????//每次新加入對象都需要調(diào)用trimToSize方法看是否需要回收
???????????trimToSize(maxSize);
???????????return createdValue;
???????}
??? }
???/**
????* Caches {@code value} for {@code key}. The value is moved to the headof
????* the queue.
????*
????* @return the previous value mapped by {@code key}.
????*/
???public final V put(K key, V value) {
???????if (key == null || value == null) {
???????????throw new NullPointerException("key == null || value ==null");
???????}
???????V previous;
???????synchronized (this) {
???????????putCount++;
???????????size += safeSizeOf(key, value);?//size加上預(yù)put對象的大小
???????????previous = map.put(key, value);
???????????if (previous != null) {
??????????????? //如果之前存在鍵為key的對象,則size應(yīng)該減去原來對象的大小
???????????????size -= safeSizeOf(key,previous);
???????????}
???????}
???????if (previous != null) {
???????????entryRemoved(false, key, previous, value);
???????}
???????//每次新加入對象都需要調(diào)用trimToSize方法看是否需要回收
???????trimToSize(maxSize);
???????return previous;
??? }
???/**
????* @param maxSize the maximum size of the cache before returning. May be-1
????*???? to evict even 0-sizedelements.
????*此方法根據(jù)maxSize來調(diào)整內(nèi)存cache的大小盲憎,如果maxSize傳入-1嗅骄,則清空緩存中的所有對象
????*/
???private void trimToSize(int maxSize) {
???????while (true) {
???????????K key;
???????????V value;
???????????synchronized (this) {
??????????????? if (size < 0 ||(map.isEmpty() && size != 0)) {
??????????????????? throw newIllegalStateException(getClass().getName()
????????????????????? ??????+ ".sizeOf() is reportinginconsistent results!");
??????????????? }
??????????????? //如果當(dāng)前size小于maxSize或者map沒有任何對象,則結(jié)束循環(huán)
??????????????? if (size <= maxSize ||map.isEmpty()) {
??????????????????? break;
??????????????? }
??????????????? //移除鏈表頭部的元素,并進(jìn)入下一次循環(huán)
??????????????? Map.Entry toEvict =map.entrySet().iterator().next();
??????????????? key = toEvict.getKey();
??????????????? value = toEvict.getValue();
??????????????? map.remove(key);
??????????????? size -= safeSizeOf(key, value);
???? ???????????evictionCount++;? //回收次數(shù)+1
???????????}
???????????entryRemoved(true, key, value, null);
???????}
??? }
???/**
????* Removes the entry for {@code key} if it exists.
????*
????* @return the previous value mapped by {@code key}.
????*從內(nèi)存緩存中根據(jù)key值移除某個對象并返回該對象
????*/
???public final V remove(K key) {
???????if (key == null) {
???????????throw new NullPointerException("key == null");
???????}
???????V previous;
???????synchronized (this) {
???????????previous = map.remove(key);
???????????if (previous != null) {
??????????????? size -= safeSizeOf(key,previous);
???????????}
???????}
???????if (previous != null) {
???????????entryRemoved(false, key, previous, null);
???????}
???????return previous;
??? }
???/**
????* 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.
????*
????*
The method is called without synchronization: other threadsmay
????* 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 bya {@link #put} or {@link #remove}.
????* @param newValue the new value for {@code key}, if it exists. Ifnon-null,
????*???? this removal was caused by a{@link #put}. Otherwise it was caused by
????*???? an eviction or a {@link#remove}.
? ???*/
???protected void entryRemoved(boolean evicted, K key, V oldValue, VnewValue) {}
???/**
????* Called after a cache miss to compute a value for the correspondingkey.
????* Returns the computed value or null if no value can be computed. The
??? ?* default implementation returns null.
????*
????*
The method is called without synchronization: other threadsmay
????* access the cache while this method is executing.
????*
????*
If a value for {@code key} exists in the cache when thismethod
????* returns, the created value will be released with {@link #entryRemoved}
????* and discarded. This can occur when multiple threads request the samekey
????* at the same time (causing multiple values to be created), or when one
????* thread calls {@link #put} while another is creating a value for thesame
????* key.
????*/
???protected V create(K key) {
???????return null;
??? }
???private int safeSizeOf(K key, V value) {
???????int result = sizeOf(key, value);
???????if (result < 0) {
???????????throw new IllegalStateException("Negative size: " + key +"=" + value);
???????}
???????return result;
??? }
???/**
????* Returns the size of the entry for {@code key} and {@code value} in
????* user-defined units.? The defaultimplementation returns 1 so that size
????* is the number of entries and max size is the maximum number ofentries.
????*
????*
An entry's size must not change while it is in the cache.
????*用來計算單個對象的大小饼疙,這里默認(rèn)返回1溺森,一般需要重寫該方法來計算對象的大小
????* xUtils中創(chuàng)建LruMemoryCache時就重寫了sizeOf方法來計算bitmap的大小
????* mMemoryCache = new LruMemoryCache(globalConfig.getMemoryCacheSize()) {
????*?????? @Override
????*?????? protected intsizeOf(MemoryCacheKey key, Bitmap bitmap) {
????*?????????? if (bitmap == null)return 0;
????*?????????? returnbitmap.getRowBytes() * bitmap.getHeight();
????*?????? }
????*?? };
????*
????*/
???protected int sizeOf(K key, V value) {
???????return 1;
??? }
???/**
????* Clear the cache, calling {@link #entryRemoved} on each removed entry.
????*清空內(nèi)存緩存
????*/
???public final void evictAll() {
???????trimToSize(-1); // -1 will evict 0-sized elements
??? }
???/**
????* For caches that do not override {@link #sizeOf}, this returns thenumber
????* of entries in the cache. For all other caches, this returns the sum of
????* the sizes of the entries in this cache.
????*/
???public synchronized final int size() {
???????return size;
??? }
???/**
????* For caches that do not override {@link #sizeOf}, this returns themaximum
????* number of entries in the cache. For all other caches, this returns the
????* maximum sum of the sizes of the entries in this cache.
????*/
???public synchronized final int maxSize() {
???????return maxSize;
??? }
???/**
????* Returns the number of times {@link #get} returned a value.
????*/
???public synchronized final int hitCount() {
???????return hitCount;
??? }
???/**
????* Returns the number of times {@link #get} returned null or required anew
????* value to be created.
????*/
???public synchronized final int missCount() {
???????return missCount;
??? }
???/**
????* Returns the number of times {@link #create(Object)} returned a value.
????*/
???public synchronized final int createCount() {
???????return createCount;
??? }
???/**
????* Returns the number of times {@link #put} was called.
????*/
???public synchronized final int putCount() {
???????return putCount;
??? }
???/**
????* Returns the number of values that have been evicted.
????*/
???public synchronized final int evictionCount() {
???????return evictionCount;
??? }
???/**
????* Returns a copy of the current contents of the cache, ordered fromleast
????* recently accessed to most recently accessed.
????*/
???public synchronized final Map snapshot() {
???????return new LinkedHashMap(map);
??? }
???@Override public synchronized final String toString() {
?? ?????int accesses = hitCount + missCount;
???????int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
???????returnString.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
??????????????? maxSize, hitCount, missCount,hitPercent);
??? }
}