标签:
Universal-Image-Loader的内存缓存策略
1. 只使用的是强引用缓存
2.使用强引用和弱引用相结合的缓存有
3.只使用弱引用缓存
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; /*开源框架默认的内存缓存类,缓存BitMap的强引用,其具体实现类似于android.support.v4.util.LruCache类, 且都是通过LinkedHashMap的委派具体实现的,这里将LruChace的K,V分别固化为了String,和Bitmap 并且简化了其中某些函数的实现*/ /*注意LinkedHashMap是非线性安全的,所以要通过synchronized['s??kr?na?zd]去手动实现线程安全*/ public class LruMemoryCache implements MemoryCache { private final LinkedHashMap<String, Bitmap> map; //定义最大Cache大小 private final int maxSize; /** cache的实际大小(单位为bytes) */ private int size; /**maxSize:cache中的Bitmap总最大size(注意这里不是个数,而是cache的内存大小值,参照下述sizeOf()函数的实现)*/ 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); /** LinkedHashMap( int initialCapacity, float loadFactor, boolean accessOrder) 参数accessOrder设置为true是很重要的, true表示即会按照访问顺序排序,最近访问的放在最前,最早访问的放在后面 参见:http://www.cnblogs.com/lzrabbit/p/3734850.html**/ } /** * get函数:注意hashMap和HashTable不同,HashMap是支持key或value为null值 * 返回cache中的Bitmap对象,同时将该Bitmap移至队列的头部(LinkedHashMap会自动实现,因为accessOrder已经设为true); * 如果cache不存在,返回null */ @Override (这里是interface MemoryCacheAware<K, V>(该类已被废弃,替代使用其子类interface MemoryCache extends MemoryCacheAware<String, Bitmap>)中的函数 ) public final Bitmap get(String key) { if (key == null) { thrownew NullPointerException("key == null"); } /*加synchronized实现线程安全*/ synchronized (this) { return map.get(key); } } /** put <String,Bitmap>键值对,并将该记录移至队列头部*/ @Override public final boolean put(String key, Bitmap value) { /*在实际应用中取出null值*/ if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } synchronized (this) { size += sizeOf(key, value); /*put是HashMap的函数,LinkedHashMap并未进行重写; 其返回该键key对应的之前的value值;若不存在,返回null*/ Bitmap previous = map.put(key, value); //这里要注意当key已经存在,更新value值,要减去previous的Bitmap的内存大小 if (previous != null) { size -= sizeOf(key, previous); } } /*添加新图像后,要注意判断是否超过cache内存最大值,进行响应调整*/ trimToSize(maxSize); return true; } /** * 删除最老的键值对,直至剩下的键值对内存总值少于或等于标准最大值 */ private void trimToSize(int maxSize) { while (true) { String key; Bitmap value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { thrownew IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } /**未超过警戒值,则无需进行处理*/ if (size <= maxSize || map.isEmpty()) { break; } /*************存储bitmap值超过警戒线****************/ /**遍历Map的常用方式,效率较高;使用迭代器进行遍历,并逐步删除末尾值,直至满足内存要求*/ /***(interface)Map.Entry is a key/value mapping contained in a Map.**/ 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); } } } /** remove函数,删除对应特定key的键值对**/ @Override public final Bitmap remove(String key) { if (key == null) { throw new NullPointerException("key == null"); } synchronized (this) { /*** HashMap.remove()函数返回值:the value of the removed mapping or null if no mapping for the specified key was found.*/ 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); // maxSize赋值为-1能够实现清空效果,但为何不直接使用Map的clear(); } /** * 返回key对应的Bitmap的大小有多少bytes * 同时注意一个键值对的size在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); } }
LruMemoryCache实际上是简单实现了Android中Lrucache,下面附上LruCache的源码:
<pre class="java" name="code">package android.support.v4.util; import java.util.LinkedHashMap; import java.util.Map; public class LruCache<K, V> { private final LinkedHashMap<K, V> map; /** Size of this cache in units. Not necessarily the number of elements. */ private int size; private int maxSize; private int putCount; //添加的记录个数 private int createCount; // private int evictionCount;//删除的记录个数 private int hitCount; //查询命中的记录个数 private int 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. */ public LruCache(intmaxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<K, V>(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 to the * head of the queue. This returns null if a value is not cached and cannot * be created. */ 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) { 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. */ /**这里是get获取的mapValue值为null的情况,用以常吃新建一个键值**/ V createdValue = create(key); if (createdValue == null) { return null; } synchronized (this) { createCount++; 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); return mapValue; } else { trimToSize(maxSize); returncreatedValue; } } /** * Caches {@code value} for {@code key}. The value is moved to the head of * 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); previous = map.put(key, value); if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, value); } trimToSize(maxSize); return previous; } /** * 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. */ public void trimToSize(intmaxSize) { while (true) { K key; V 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<K, V> toEvict = map.entrySet().iterator().next(); 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}. */ public final V remove(K key) { if (key == null) { thrownew 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. * * <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}. */ protected void entryRemoved(boolean evicted, 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. */ 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 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. */ protected int sizeOf(K key, V value) { return 1; } /** * Clear the cache, calling {@link #entryRemoved} on each removed entry. */ public final 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. */ public synchronized final intsize() { return size; } /** * 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. */ 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 a new * 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 from least * recently accessed to most recently accessed. */ public synchronized final Map<K, V> snapshot() { return new LinkedHashMap<K, V>(map); } @Override public synchronized final String toString() { int accesses = hitCount + missCount; int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0; return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", maxSize, hitCount, missCount, hitPercent); } }
Android开源框架Universal-Image-Loader学习二——LruMemoryCache源码阅读
标签:
原文地址:http://blog.csdn.net/woliuyunyicai/article/details/45330867