您好,登錄后才能下訂單哦!
圖片內存緩存可以提高圖片顯示速度,但是有些問題,比如占用內存,如果不加以控制,甚至可能會OOM
所以,需要提供各種各樣的算法來控制內存的使用,以適應不同的使用場景,目前,ImageLoader提供了若干內存管理算法。
默認內存緩存是關閉的,需要手動打開
算法 | 解釋 |
---|---|
MemoryCache | Interface 內存緩存的接口 |
MemoryCache | Interface 內存緩存的接口 |
FuzzyKeyMemoryCache | 模糊Key內存緩存,一些不同的Key可能會被認為是相同(通過equals方法)。當你put一個值到cache里面時,equals相同的Key也會被替換。這是一個內部功能,通常我們不需要使用到它。 |
LimitedAgeMemoryCache | 限制時間內存緩存,如果一些對象的使用時間超過了定義的值,那么就會移除。比如最大時間設置成60s,那么如果一個對象是60s前put進來的,那么它就會失效。 |
LruMemoryCache | 一個強引用的圖片,限制圖片使用數量的內存緩存,每個被訪問的圖片都會移動到隊列的頭部。當一個圖片添加到一個滿的隊列的時候,隊尾的圖片將會被驅逐而被垃圾回收器回收。 |
BaseMemoryCache | 抽象類,基本的圖片緩存管理,提供了圖片存儲(強引用或者弱引用),具體的可以看下面的MemoryCache實現。 |
WeakMemoryCache | 繼承自BaseMemoryCache,弱引用內存緩存,弱引用,當內存不足的時候,會被系統回收。 |
LimitedMemoryCache | 抽象類,繼承自BaseMemoryCache,限制內存的內存管理抽象類,包括下面4個具體實現。提供圖片對象存儲,存儲的所有的圖片大小將不會超過大小限制。這個緩存提供了強引用和弱引用存儲了圖片,強引用用來限制圖片大小,弱引用用來其他緩存的圖片。 |
FIFOLimitedMemoryCache | 繼承自LimitedMemoryCache,存儲的圖片大小不小超過限定的大小。當緩存圖片到達指定大小時,會按照FIFO(先進先出)的原則清理圖片。 |
LRULimitedMemoryCache | 繼承自LimitedMemoryCache,存儲的圖片大小不小超過限定的大小。當緩存圖片到達指定大小時,最早使用的圖片會從緩存中刪除。 |
LargestLimitedMemoryCache | 繼承自LimitedMemoryCache,存儲的圖片大小不小超過限定的大小。當緩存圖片到達指定大小時,最大的圖片會從緩存中刪除。 |
UsingFreqLimitedMemoryCache | 繼承自LimitedMemoryCache,存儲的圖片大小不小超過限定的大小。當緩存圖片到達指定大小時,最少使用頻率的圖片會從緩存中刪除。 |
下面,我們會具體看各個MemoryCache的實現
public class LimitedAgeMemoryCache implements MemoryCache {
private final MemoryCache cache;
private final long maxAge;
private final Map<String, Long> loadingDates = Collections.synchronizedMap(new HashMap<String, Long>());
@Override
public boolean put(String key, Bitmap value) {
boolean putSuccesfully = cache.put(key, value);
if (putSuccesfully) {
loadingDates.put(key, System.currentTimeMillis());
}
return putSuccesfully;
}
@Override
public Bitmap get(String key) {
Long loadingDate = loadingDates.get(key);
if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) {
cache.remove(key);
loadingDates.remove(key);
}
return cache.get(key);
}
}
實現的方法很簡單,在put的時候,用一個Map記錄了存入的時間,get的時候,判斷時間是否大于maxAge,如果是,那么就刪掉這個緩存。
public class LruMemoryCache implements MemoryCache {
private final LinkedHashMap<String, Bitmap> map;
private final int maxSize;
@Override
public final boolean put(String key, Bitmap value) {
...
synchronized (this) {
size += sizeOf(key, value);
Bitmap previous = map.put(key, value);
if (previous != null) {
size -= sizeOf(key, previous);
}
}
trimToSize(maxSize);
return true;
}
private void trimToSize(int maxSize) {
while (true) {
String key;
Bitmap value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= sizeOf(key, value);
}
}
}
}
實現的原理是利用LinkedHashMap,先存入的key在遍歷的時候在鏈表的頭部,所以在put值的時候,從頭開始遍歷HashMap,從頭刪除圖片緩存,直到內存的大小在指定大小以內。
另外為了防止多線程的問題,put和trimToSize刪除圖片的時候做了synchronized同步。
這個也是ImageLoader使用比較多的一種內存管理方法。
public class WeakMemoryCache extends BaseMemoryCache {
@Override
protected Reference<Bitmap> createReference(Bitmap value) {
return new WeakReference<Bitmap>(value);
}
}
實現方法很簡單,用一個弱引用就可以解決,用弱引用包裹了圖片。
FIFOLimitedMemoryCache繼承自抽象類LimitedMemoryCache,所以看算法實現前還要看LimitedMemoryCache代碼。
抽象類LimitedMemoryCache和其實現子類的關系是LimitedMemoryCache put的時候限定超過大小時調用removeNext刪除圖片緩存;
但是具體怎么刪,刪除什么圖片緩存,就由子類實現。
public abstract class LimitedMemoryCache extends BaseMemoryCache {
@Override
public boolean put(String key, Bitmap value) {
boolean putSuccessfully = false;
// Try to add value to hard cache
int valueSize = getSize(value);
int sizeLimit = getSizeLimit();
int curCacheSize = cacheSize.get();
if (valueSize < sizeLimit) {
while (curCacheSize + valueSize > sizeLimit) {
Bitmap removedValue = removeNext();
if (hardCache.remove(removedValue)) {
curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
}
}
hardCache.add(value);
cacheSize.addAndGet(valueSize);
putSuccessfully = true;
}
// Add value to soft cache
super.put(key, value);
return putSuccessfully;
}
}
public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());
...
@Override
protected Bitmap removeNext() {
return queue.remove(0);
}
}
用list實現,直接從頭開始移除。
public class LRULimitedMemoryCache extends LimitedMemoryCache {
/** Cache providing Least-Recently-Used logic */
private final Map<String, Bitmap> lruCache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(INITIAL_CAPACITY, LOAD_FACTOR, true));
@Override
protected Bitmap removeNext() {
Bitmap mostLongUsedValue = null;
synchronized (lruCache) {
Iterator<Entry<String, Bitmap>> it = lruCache.entrySet().iterator();
if (it.hasNext()) {
Entry<String, Bitmap> entry = it.next();
mostLongUsedValue = entry.getValue();
it.remove();
}
}
return mostLongUsedValue;
}
}
使用一個 LinkedHashMap lruCache來保存圖片的put順序,需要移除的時候(removeNext)從lruCache找到第一張圖片即可。
public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
@Override
public Bitmap get(String key) {
Bitmap value = super.get(key);
// Increment usage count for value if value is contained in hardCahe
if (value != null) {
Integer usageCount = usingCounts.get(value);
if (usageCount != null) {
usingCounts.put(value, usageCount + 1);
}
}
return value;
}
@Override
protected Bitmap removeNext() {
Integer minUsageCount = null;
Bitmap leastUsedValue = null;
Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet();
synchronized (usingCounts) {
for (Entry<Bitmap, Integer> entry : entries) {
if (leastUsedValue == null) {
leastUsedValue = entry.getKey();
minUsageCount = entry.getValue();
} else {
Integer lastValueUsage = entry.getValue();
if (lastValueUsage < minUsageCount) {
minUsageCount = lastValueUsage;
leastUsedValue = entry.getKey();
}
}
}
}
usingCounts.remove(leastUsedValue);
return leastUsedValue;
}
}
使用一個HashMap usingCounts來存儲bitmap的使用次數,每次get的時候都會讓使用次數+1,那需要移除的時候圖片緩存的時候,遍歷usingCounts,找到使用次數最少的圖片,刪除之。
不過這種遍歷的算法感覺效率不高,慎用!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。