全面解析Android的开源图片框架Universal-Image-Loader

2019-12-10 18:34:04丽君

首先我们来了解下什么是强引用和什么是弱引用?
强引用是指创建一个对象并把这个对象赋给一个引用变量, 强引用有引用变量指向时永远不会被垃圾回收。即使内存不足的时候宁愿报OOM也不被垃圾回收器回收,我们new的对象都是强引用
弱引用通过weakReference类来实现,它具有很强的不确定性,如果垃圾回收器扫描到有着WeakReference的对象,就会将其回收释放内存

现在我们来看Universal-Image-Loader有哪些内存缓存策略
1. 只使用的是强引用缓存 
LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用,下面我会从源码上面分析这个类)
2.使用强引用和弱引用相结合的缓存有
UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)
LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)
3.只使用弱引用缓存
WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
上面介绍了Universal-Image-Loader所提供的所有的内存缓存的类,当然我们也可以使用我们自己写的内存缓存类,我们还要看看要怎么将这些内存缓存加入到我们的项目中,我们只需要配置ImageLoaderConfiguration.memoryCache(...),如下

ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this) 
  .memoryCache(new WeakMemoryCache()) 
  .build(); 

下面我们来分析LruMemoryCache这个类的源代码

package com.nostra13.universalimageloader.cache.memory.impl; 
 
import android.graphics.Bitmap; 
import com.nostra13.universalimageloader.cache.memory.MemoryCacheAware; 
 
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 MemoryCacheAware<String, Bitmap> { 
 
 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 void 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); 
   } 
  } 
 } 
 
 @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); 
 } 
}