UniversalImageLoader源码解读04-内存缓存


      写到这里,我们越来越接近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()没有对父类的图片做删除,这是不是说,内存泄漏了呢??? 父类没有提供接口,而且这个方法还是父类留给我们去实现的,希望有人能帮我解答这个问题


期待...................................................


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值