Android圖片緩存及緩存算法(Universal-Image-Loader)

內(nèi)存緩存

緩存與內(nèi)存回收機(jī)制有關(guān),java中有四種與垃圾回收(gc)有關(guān)的引用:強(qiáng)引用(StrongReference)综液、軟引用(SoftReference)、弱引用(WeakReference)和虛引用(PhantomReference)。

強(qiáng)引用(StrongReference)

強(qiáng)引用是最普遍的一種引用曹锨,在java中使用new關(guān)鍵字生成的對象就是強(qiáng)引用,對于強(qiáng)引用亩冬,java垃圾回收機(jī)制不會(huì)將其回收艘希,當(dāng)內(nèi)存不足時(shí),java虛擬機(jī)寧肯拋出OutOfMemoryError錯(cuò)誤也不會(huì)隨意回收強(qiáng)引用的對象硅急。

軟引用(SoftReference)

對于軟引用覆享,當(dāng)內(nèi)存足夠時(shí),不會(huì)回收軟引用的對象营袜,但是當(dāng)內(nèi)存空間不足時(shí)撒顿,就會(huì)回收這些對象。

弱引用(WeakReference)

弱引用荚板,相對于軟引用來說凤壁,更容易被回收機(jī)制回收,當(dāng)垃圾回收機(jī)制掃描內(nèi)存空間時(shí)跪另,如果發(fā)現(xiàn)弱引用對象拧抖,不論當(dāng)前的內(nèi)存是否充足,都會(huì)立即將其回收免绿,釋放內(nèi)存空間唧席。

虛引用(PhantomReference)

虛引用不同于其他引用,其并不會(huì)決定對象的生命周期。如果一個(gè)對象僅持有虛引用淌哟,那么它就和沒有任何引用一樣迹卢,在任何時(shí)候都可能被垃圾回收器回收。

Universal-Image-Loader中的緩存機(jī)制

1徒仓、LRU(Least Recently Used)最近最久未使用頁面置換算法

LRU是操作系統(tǒng)中頁面置換算法的一種腐碱,其核心是,對于每一個(gè)頁面掉弛,設(shè)置一個(gè)時(shí)間字段症见,用來記錄這個(gè)頁面自上次被訪問以來所經(jīng)歷的時(shí)間t,當(dāng)出現(xiàn)缺頁中斷(也就是內(nèi)存不足)時(shí)狰晚,將t值最大的頁面刪除筒饰。

2、Universal-Image-Loader中緩存機(jī)制

(1)只使用的是強(qiáng)引用緩存

LruMemoryCache(使用LRU算法壁晒,緩存對象的是bitmap的強(qiáng)引用)

(2)使用強(qiáng)引用和弱引用相結(jié)合的緩存有

UsingFreqLimitedMemoryCache(如果緩存的圖片總量超過限定值瓷们,先刪除使用頻率最小的bitmap)

LRULimitedMemoryCache(這個(gè)也是使用的lru算法,和LruMemoryCache不同的是秒咐,他緩存的是bitmap的弱引用)

FIFOLimitedMemoryCache(先進(jìn)先出的緩存策略谬晕,當(dāng)超過設(shè)定值,先刪除最先加入緩存的bitmap)

LargestLimitedMemoryCache(當(dāng)超過緩存限定值携取,先刪除最大的bitmap對象)

LimitedAgeMemoryCache(當(dāng) bitmap加入緩存中的時(shí)間超過我們設(shè)定的值攒钳,將其刪除)

(3)只使用弱引用緩存

WeakMemoryCache(這個(gè)類緩存bitmap的總大小沒有限制,唯一不足的地方就是不穩(wěn)定雷滋,緩存的圖片容易被回收掉)

LruMemoryCache源碼分析

package com.nostra13.universalimageloader.cache.memory.impl;

import android.graphics.Bitmap;

import com.nostra13.universalimageloader.cache.memory.MemoryCache;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
 * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
 * become eligible for garbage collection.<br />
 * <br />
 * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.8.1
 */
public class LruMemoryCache implements MemoryCache {

    private final LinkedHashMap<String, Bitmap> map;

    private final int maxSize;
    /** Size of this cache in bytes */
    private int size;

    /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
    public LruMemoryCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
    }

    /**
     * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
     * of the queue. This returns null if a Bitmap is not cached.
     */
    @Override
    public final Bitmap get(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        synchronized (this) {
            return map.get(key);
        }
    }

    /** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
    @Override
    public final boolean put(String key, Bitmap value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        synchronized (this) {
            size += sizeOf(key, value);
            Bitmap previous = map.put(key, value);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
        }

        trimToSize(maxSize);
        return true;
    }

    /**
     * Remove the eldest entries until the total of remaining entries is at or below the requested size.
     *
     * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
     */
    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);
            }
        }
    }

    /** Removes the entry for {@code key} if it exists. */
    @Override
    public final Bitmap remove(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        synchronized (this) {
            Bitmap previous = map.remove(key);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
            return previous;
        }
    }

    @Override
    public Collection<String> keys() {
        synchronized (this) {
            return new HashSet<String>(map.keySet());
        }
    }

    @Override
    public void clear() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }

    /**
     * Returns the size {@code Bitmap} in bytes.
     * <p/>
     * An entry's size must not change while it is in the cache.
     */
    private int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public synchronized final String toString() {
        return String.format("LruCache[maxSize=%d]", maxSize);
    }
}

在源碼中LinkedHashMap作為LRU算法的緩存不撑,LinkedHashMap是HashMap的一個(gè)子類,它保留插入的順序晤斩,LinkedHashMap實(shí)現(xiàn)與HashMap的不同之處在于焕檬,后者維護(hù)著一個(gè)運(yùn)行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序澳泵,該迭代順序可以是插入順序或者是訪問順序实愚。

默認(rèn)是按插入順序排序,如果指定按訪問順序排序兔辅,那么調(diào)用get方法后腊敲,會(huì)將這次訪問的元素移至鏈表尾部,不斷訪問可以形成按訪問順序排序的鏈表维苔∨龈ǎ可以重寫removeEldestEntry方法返回true值指定插入元素時(shí)移除最老的元素。

this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);

構(gòu)造方法中第一個(gè)參數(shù)代表LinkedHashMap初始容量介时,第二個(gè)參數(shù)0.75f是加載因子乎赴,第三個(gè)參數(shù)是訪問順序忍法,默認(rèn)為false即插入順序,true為訪問順序榕吼。
LruMemoryCache中維護(hù)了一個(gè)所有bitmap的size和,如果size超過設(shè)定的maxSize勉失,就會(huì)將最近最久未使用的圖片緩存刪除羹蚣。

在源碼的put方法中調(diào)用了HashMap中的put方法:

public V put(K key, V value) {  
        if (key == null)  
            return putForNullKey(value);  
        int hash = hash(key.hashCode());  
        int i = indexFor(hash, table.length);  
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
            Object k;  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
                V oldValue = e.value;  
                e.value = value;  
                e.recordAccess(this);  
                return oldValue;  
            }  
        }  
  
        modCount++;  
        addEntry(hash, key, value, i);  
        return null;  
    } 
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            //當(dāng)LinkedHashMap按訪問排序時(shí)
            if (lm.accessOrder) {
                lm.modCount++;
                //移除當(dāng)前節(jié)點(diǎn)
                remove();
                //將當(dāng)前節(jié)點(diǎn)插入到頭結(jié)點(diǎn)前面
                addBefore(lm.header);
            }
        }
/**
        * 移除節(jié)點(diǎn),并修改前后引用
        */
       private void remove() {
           before.after = after;
           after.before = before;
       }

       /**
        * 將當(dāng)前節(jié)點(diǎn)插入到existingEntry的前面
        */
       private void addBefore(Entry<K,V> existingEntry) {
           after  = existingEntry;
           before = existingEntry.before;
           before.after = this;
           after.before = this;
       }

HashMap中put方法源碼乱凿,當(dāng)HashMap中存在這個(gè)value時(shí)顽素,會(huì)將這個(gè)value返回,否則返回null徒蟆,所以當(dāng)map.put(key, value)返回值不為空時(shí)胁出,需要減去剛加入的圖片size。當(dāng)返回為null時(shí)段审,將bitmap加入到LinkedHashMap中全蝶,返回為bitmap時(shí),直接使用該bitmap寺枉。recordAccess(HashMap<K,V> m)在HashMap的put和get方法中抑淫,會(huì)調(diào)用該方法,在HashMap中該方法為空姥闪,在LinkedHashMap中始苇,當(dāng)按訪問順序排序時(shí),該方法會(huì)將當(dāng)前節(jié)點(diǎn)插入到鏈表尾部(頭結(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn))筐喳,否則不做任何事(最近使用的bitmap會(huì)移動(dòng)到雙向鏈表的尾部)催式。

LRU算法實(shí)現(xiàn)的核心是private void trimToSize(int maxSize)函數(shù)中不斷判斷當(dāng)前size是否大于maxSize,當(dāng)超過maxSize時(shí)避归,會(huì)將LinkedHashMap中的第一個(gè)元素刪除荣月,其就是最近最久未使用的bitmap。

使用LinkedHashMap可以很容易構(gòu)造一個(gè)基于LRU算法的緩存槐脏。

UsingFreqLimitedMemoryCache源碼分析

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * 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 com.nostra13.universalimageloader.cache.memory;

import android.graphics.Bitmap;

import java.lang.ref.Reference;
import java.util.*;

/**
 * Base memory cache. Implements common functionality for memory cache. Provides object references (
 * {@linkplain Reference not strong}) storing.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.0.0
 */
public abstract class BaseMemoryCache implements MemoryCache {

    /** Stores not strong references to objects */
    private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());

    @Override
    public Bitmap get(String key) {
        Bitmap result = null;
        Reference<Bitmap> reference = softMap.get(key);
        if (reference != null) {
            result = reference.get();
        }
        return result;
    }

    @Override
    public boolean put(String key, Bitmap value) {
        softMap.put(key, createReference(value));
        return true;
    }

    @Override
    public Bitmap remove(String key) {
        Reference<Bitmap> bmpRef = softMap.remove(key);
        return bmpRef == null ? null : bmpRef.get();
    }

    @Override
    public Collection<String> keys() {
        synchronized (softMap) {
            return new HashSet<String>(softMap.keySet());
        }
    }

    @Override
    public void clear() {
        softMap.clear();
    }

    /** Creates {@linkplain Reference not strong} reference of value */
    protected abstract Reference<Bitmap> createReference(Bitmap value);
}

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * 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 com.nostra13.universalimageloader.cache.memory;

import android.graphics.Bitmap;

import com.nostra13.universalimageloader.utils.L;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Limited cache. Provides object storing. Size of all stored bitmaps will not to exceed size limit (
 * {@link #getSizeLimit()}).<br />
 * <br />
 * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
 * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @see BaseMemoryCache
 * @since 1.0.0
 */
public abstract class LimitedMemoryCache extends BaseMemoryCache {

    private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
    private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;

    private final int sizeLimit;

    private final AtomicInteger cacheSize;

    /**
     * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
     * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
     * time)
     */
    private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());

    /** @param sizeLimit Maximum size for cache (in bytes) */
    public LimitedMemoryCache(int sizeLimit) {
        this.sizeLimit = sizeLimit;
        cacheSize = new AtomicInteger();
        if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
            L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB);
        }
    }

    @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;
    }

    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            if (hardCache.remove(value)) {
                cacheSize.addAndGet(-getSize(value));
            }
        }
        return super.remove(key);
    }

    @Override
    public void clear() {
        hardCache.clear();
        cacheSize.set(0);
        super.clear();
    }

    protected int getSizeLimit() {
        return sizeLimit;
    }

    protected abstract int getSize(Bitmap value);

    protected abstract Bitmap removeNext();
}

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * 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 com.nostra13.universalimageloader.cache.memory.impl;

import android.graphics.Bitmap;
import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
 * exceed size limit. When cache reaches limit size then the bitmap which used the least frequently is deleted from
 * cache.<br />
 * <br />
 * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
 * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.0.0
 */
public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
    /**
     * Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache
     * size will exceed limit then object with the least frequently usage is deleted (but it continue exist at
     * {@link #softMap} and can be collected by GC at any time)
     */
    private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());

    public UsingFreqLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            usingCounts.put(value, 0);
            return true;
        } else {
            return false;
        }
    }

    @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
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            usingCounts.remove(value);
        }
        return super.remove(key);
    }

    @Override
    public void clear() {
        usingCounts.clear();
        super.clear();
    }

    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @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;
    }

    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

UsingFreqLimitedMemoryCache繼承自LimitedMemoryCach囊拜,而LimitedMemoryCach繼承自BaseMemoryCache躲庄。BaseMemoryCache中維護(hù)了Map<String, Reference<Bitmap>> softMap包含軟引用類型bitmap的softMap,定義了softMap的get、put占哟、remove和clear方法。

LimitedMemoryCache繼承BaseMemoryCache章贞,其中定義了MAX_NORMAL_CACHE_SIZE_IN_MB页响、MAX_NORMAL_CACHE_SIZE、內(nèi)存大小限制sizeLimit和List<Bitmap> hardCache(用來維護(hù)該算法的bitmap列表)鸟缕。當(dāng)進(jìn)行put操作時(shí)晶框,首先判斷put的bitmap是否比限制的sizeLimit還大排抬,如果小于sizeLimit,循環(huán)判斷當(dāng)前緩存的cacheSize(AtomicInteger是一個(gè)提供原子操作的Integer類授段,通過線程安全的方式操作加減蹲蒲。AtomicInteger提供原子操作來進(jìn)行Integer的使用,因此十分適合高并發(fā)情況下的使用侵贵。)加上要put的bitmap的size是否大于sizeLimit届搁,如果大于sizeLimit,則調(diào)用算法刪除頻率最小的bitmap窍育。

removeNext()卡睦,在UsingFreqLimitedMemoryCache中實(shí)現(xiàn),其使用Map<Bitmap, Integer> usingCounts保存內(nèi)存中bitmap的使用次數(shù)漱抓。當(dāng)調(diào)用removeNext()方法時(shí)表锻,遍歷Map<Bitmap, Integer> usingCounts,找到使用次數(shù)最少的bitmap并返回乞娄。

FIFOLimitedMemoryCache源碼分析

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * 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 com.nostra13.universalimageloader.cache.memory.impl;

import android.graphics.Bitmap;
import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
 * exceed size limit. When cache reaches limit size then cache clearing is processed by FIFO principle.<br />
 * <br />
 * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
 * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.0.0
 */
public class FIFOLimitedMemoryCache extends LimitedMemoryCache {

    private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());

    public FIFOLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            queue.add(value);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            queue.remove(value);
        }
        return super.remove(key);
    }

    @Override
    public void clear() {
        queue.clear();
        super.clear();
    }

    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    protected Bitmap removeNext() {
        return queue.remove(0);
    }

    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

FIFOLimitedMemoryCache也繼承了LimitedMemoryCache瞬逊,并維護(hù)了一個(gè)LinkedList<Bitmap>,與ArrayList相比补胚,LinkedList中包含了一個(gè)雙向循環(huán)鏈表码耐,提供了類似棧和隊(duì)列的操作。

LinkedList的add方法:

 public boolean add(E e) {
     addBefore(e, header);
     return true;
 }
 private Entry<E> addBefore(E e, Entry<E> entry) {
     Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
     newEntry.previous.next = newEntry;
     newEntry.next.previous = newEntry;
     size++;
     modCount++;
     return newEntry;
 }

向LinkedList中插入元素時(shí)溶其,是將元素插入到了header的后面addBefore(e, header);骚腥,即將元素插入到鏈表的尾部。

public E remove(int index) {
    return remove(entry(index));
}

private Entry<E> entry(int index) {
         if (index < 0 || index >= size)
             throw new IndexOutOfBoundsException("Index: "+index+
                                                 ", Size: "+size);
         Entry<E> e = header;
         // 根據(jù)這個(gè)判斷決定從哪個(gè)方向遍歷這個(gè)鏈表
         if (index < (size >> 1)) {
             for (int i = 0; i <= index; i++)
                 e = e.next;
        } else {
            // 可以通過header節(jié)點(diǎn)向前遍歷瓶逃,說明這個(gè)一個(gè)循環(huán)雙向鏈表束铭,header的previous指向鏈表的最后一個(gè)節(jié)點(diǎn),這也驗(yàn)證了構(gòu)造方法中對于header節(jié)點(diǎn)的前后節(jié)點(diǎn)均指向自己的解釋
            for (int i = size; i > index; i--)
                e = e.previous;
        }
       return e;
    }

FIFOLimitedMemoryCache的removeNext()方法中queue.remove(0);是將雙向循環(huán)列表的第一個(gè)元素刪除厢绝。由此可知由于插入元素時(shí)是向雙向循環(huán)列表中尾部插入新元素契沫,刪除時(shí),刪除列表的第一個(gè)元素昔汉,即先進(jìn)先出算法懈万。
除此之外,LargestLimitedMemoryCache和LimitedAgeMemoryCach都繼承了LimitedMemoryCache靶病,是強(qiáng)引用和弱引用相結(jié)合的緩存会通,因?yàn)锽aseMemoryCache中維護(hù)了一個(gè)軟引用的HashMap<String, Reference<Bitmap>>(),LimitedMemoryCache中維護(hù)了一個(gè)強(qiáng)引用的LinkedList<Bitmap>()娄周,只是具體實(shí)現(xiàn)算法不同而已涕侈。

WeakMemoryCache源碼分析

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * 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 com.nostra13.universalimageloader.cache.memory.impl;

import android.graphics.Bitmap;
import com.nostra13.universalimageloader.cache.memory.BaseMemoryCache;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;

/**
 * Memory cache with {@linkplain WeakReference weak references} to {@linkplain android.graphics.Bitmap bitmaps}<br />
 * <br />
 * <b>NOTE:</b> This cache uses only weak references for stored Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.5.3
 */
public class WeakMemoryCache extends BaseMemoryCache {
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

WeakMemoryCache繼承BaseMemoryCache,返回一個(gè) WeakReference<Bitmap>煤辨,其他方法使用父類方法裳涛。

參考文章:
http://blog.csdn.net/xiaanming/article/details/27525741
http://www.cnblogs.com/children/archive/2012/10/02/2710624.html
http://blog.csdn.net/jzhf2012/article/details/8540543

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末木张,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子端三,更是在濱河造成了極大的恐慌舷礼,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郊闯,死亡現(xiàn)場離奇詭異且轨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)虚婿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泳挥,“玉大人然痊,你說我怎么就攤上這事√敕” “怎么了剧浸?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長矗钟。 經(jīng)常有香客問我唆香,道長,這世上最難降的妖魔是什么吨艇? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任躬它,我火速辦了婚禮,結(jié)果婚禮上东涡,老公的妹妹穿的比我還像新娘冯吓。我一直安慰自己,他們只是感情好疮跑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布组贺。 她就那樣靜靜地躺著,像睡著了一般祖娘。 火紅的嫁衣襯著肌膚如雪失尖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天渐苏,我揣著相機(jī)與錄音掀潮,去河邊找鬼。 笑死整以,一個(gè)胖子當(dāng)著我的面吹牛胧辽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播公黑,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼邑商,長吁一口氣:“原來是場噩夢啊……” “哼摄咆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起人断,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吭从,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后恶迈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涩金,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年暇仲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了步做。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奈附,死狀恐怖全度,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斥滤,我是刑警寧澤将鸵,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站佑颇,受9級特大地震影響顶掉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挑胸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一痒筒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗜暴,春花似錦凸克、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舆逃,卻和暖如春蚂维,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背路狮。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工虫啥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奄妨。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓涂籽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砸抛。 傳聞我的和親對象是個(gè)殘疾皇子评雌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容