写到这里,我们越来越接近UniversalImageLoader的核心的,内存缓存和磁盘缓存是它的主要功能, 此外还有调度功能,也是很重要的一部分
在com.nostra13.universalimageloader.cache.memory 包和com.nostra13.universalimageloader.cache.memory.impl 下
核心接口:
public interface MemoryCache {
boolean put(String key, Bitmap value);//将图片加入内存缓存
Bitmap get(String key);//从内存缓存取出一个图片
Bitmap remove(String key);//从内存缓存删除一个图片
Collection<String> keys();// 返回被缓存的图片的key
void clear();//清空内存缓存
}
这个框架的好处就是抽象,频繁使用接口,因此对于我们,其实理解接口也就理解了框架。但是我们还需要讨论一下细节实现,不是么。
BaseMemoryCache 实现了这个MemoryCache接口
public abstract class BaseMemoryCache implements MemoryCache {
/** 保存图片的弱引用,这个softMap 就是cache,是线程安全的 */
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();
}
/**
* 子类可以自由选择非强引用的类型
*/
protected abstract Reference<Bitmap> createReference(Bitmap value);
}
你至少应该知道Collections.synchronizedMap()返回一个线程安全的HashMap
因为UniversalImageLoader会管理缓存防止OOM,那么缓存的大小一定不能是无限大的,尤其是对于内存缓存而言,意义重大。
所以有必要实现一个大小有限的内存缓存,很庆幸,框架为我们提供了实现LimitedMemoryCache ,我们看一下关键点
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;//当前内存缓存使用的字节
private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());
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(removedValue == null){
L.e("removedValue == null break, multi-thread error may happen.");
break;
}
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();//留给子类实现,从缓存中清除一张暂时不用的图片
}
这里又定义了一个缓存,我们知道之前的缓存是软引用,这个是强引用,目的是为了不让垃圾回收马上回收掉,这样在没有外部强引用引用图片的时候,只要我们清除强引用的图片,就能控制图片的回收。
@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(removedValue == null){
L.e("removedValue == null break, multi-thread error may happen.");
break;
}
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;
}
这段代码即使使用了多线程保护机制,比如使用了AtomicInteger,但是真正安全吗??读者自己想一下,假设一下多个线程同时执行put操作,看看会不会有问题,同理remove下面介绍一个具体实现内存缓存的类,框架为我们提供了这么多的策略供我们选择,全部在com.nostra13.universalimageloader.cache.memory.impl包下
这里只介绍最简单的两个:FIFOLimitedMemoryCache WeakMemoryCache 他们的代码实在是太简单了
public class WeakMemoryCache extends BaseMemoryCache {
@Override
protected Reference<Bitmap> createReference(Bitmap value) {
return new WeakReference<Bitmap>(value);
}
}
如果你的应用采用了这个类,意味着我们失去了控制图片回收的权利,不推荐使用,因为没有缓存大小的限制,所以使用了弱引用,图片不用就自动被回收
public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());
public FIFOLimitedMemoryCache(int sizeLimit) {
super(sizeLimit);
}
@Override
public synchronized boolean put(String key, Bitmap value) {
if (super.put(key, value)) {
queue.add(value);
return true;
} else {
return false;
}
}
@Override
public synchronized Bitmap remove(String key) {
Bitmap value = super.get(key);
if (value != null) {
queue.remove(value);
}
return super.remove(key);
}
@Override
public synchronized void clear() {
queue.clear();
super.clear();
}
@Override
protected int getSize(Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
@Override
protected synchronized Bitmap removeNext() {
try{
return queue.remove(0);
}catch(Exception e){
return null;
}
}
@Override
protected Reference<Bitmap> createReference(Bitmap value) {
return new WeakReference<Bitmap>(value);
}
}
笔者推荐使用这个类,这里明确一下,这里又用了一个内存缓存queue,但是只是保存的引用,因此不会影响。
这里留一个bug,其实UniversalImageLoader是有bug的,大家注意一下removeNext()这个方法, 源码没有做判断,笔者在使用的时候抛了异常,try是笔者加的,
removeNext()没有对父类的图片做删除,这是不是说,内存泄漏了呢??? 父类没有提供接口,而且这个方法还是父类留给我们去实现的,希望有人能帮我解答这个问题
期待...................................................