一、简介
UniversalImageLoader是一个开源图片加载库,内部使用了内存缓存、本地缓存、网络下载实现了加载bitmap的功能;内存缓存有LruMemoryCache(默认)等缓存策略。
二、基础用法
// 1.设置加载图片的选项
DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(false)
.bitmapConfig(Bitmap.Config.ARGB_8888)
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
.considerExifParams(true)
.build();
// 2.加载图片
ImageLoader.getInstance().loadImage(netUrl, displayImageOptions,
new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) { }
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) { }
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { }
@Override
public void onLoadingCancelled(String imageUri, View view) { }
});
复制代码
三、源码分析
源码从ImageLoader.getInstance().loadImage()开始分析:
public class ImageLoader {
// 调用入口
public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
checkConfiguration();
if (targetImageSize == null) {
targetImageSize = configuration.getMaxImageSize();
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
displayImage(uri, imageAware, options, listener, progressListener);
}
// 加载图片
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
// 1.内存缓存中是否存在,默认为LruMemoryCache
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
// ...省略部分代码
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
} else {
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
// 封装成LoadAndDisplayImageTask从本地缓存查找/发送网络请求
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}
}
复制代码
四、内存缓存
内存缓存LruMemoryCache是通过LinkedHashMap实现了最近最少使用算法。
public class LruMemoryCache implements MemoryCache {
private final LinkedHashMap<String, Bitmap> map;
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);
}
@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;
}
@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;
}
}
}
复制代码
五、LinkedHashMap实现LRU
LRU是最近最少使用算法,实现的算法有多种,比如对每个数据都新增一个时间戳字段等;考虑到算法执行效率,一般采用链表+HashMap的实现方式。
LinkedHashMap内部实现LRU是通过HashMap+双链表方式实现的,当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部,如果不存在,则新建一个节点,放到链表头部。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样,头节点存放最近被访问的数据项,尾节点存放最久未访问的数据项,若缓存满了,则删除尾节点即可。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
// 头节点,最近被访问的数据
transient LinkedHashMapEntry<K,V> head;
// 尾节点,最久未访问的数据
transient LinkedHashMapEntry<K,V> tail;
// 获取某个节点
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
// 在节点被访问后,更新头节点
afterNodeAccess(e);
return e.value;
}
// 在节点被访问后,更新头节点
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMapEntry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMapEntry<K,V> p =
(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END