主要分为2种缓存,一种是内存缓存,一种是磁盘缓存
加载一张图片的时候,获取顺序:
Lru算法缓存
【--->】
弱引用缓存
【--->】
磁盘缓存
源码解析如下:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
//1.使用 LRU 机制从内存获取,成功则返回,获取不到,下一步
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
//2.使用弱引用机制从内存获取,成功则返回,获取不到,下一步
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
//创建一个 runnable 下一步提交给线程池使用,run()方法就是线程池要执行的东东
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
}
// runnable
class EngineRunnable implements Runnable, Prioritized {
@Override
public void run() {
try {
//执行 decode()方法
resource = decode();
} catch (Exception e) {
//...
}
//...
}
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
//3.从本地缓存获取
return decodeFromCache();
} else {
//4. 从网络请求
return decodeFromSource();
}
}
}
从网络获取加载图片成功 存储的顺序是:
网络加载
【--->】
磁盘缓存
【--->】
弱引用缓存
【--->】
Lru算法缓存
首先从网络加载图片
若设置缓存原始图片
【1.1】磁盘存储 ,否则跳过这一步
没有设置则
【1.2】存储转换过的图片
【2】图片显示成功,立即放入弱引用缓存
【3】 比如页面退出,则将当前页面的图片数据从弱引用缓存移除,放如LRU缓存
class DecodeJob<A, T, Z> {
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
// 这是网络请求图片
final A data = fetcher.loadData(priority);
//【1.1】磁盘缓存 这儿是可选的: 存储图片的原始数据,没有处理过的
//下一步1.2】
decoded = decodeFromSourceData(data);
} finally {
}
return decoded;
}
private void writeTransformedToCache(Resource<T> transformed) {
SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
// 【1.2】 磁盘缓存 这是存储转换过的图片数据
//下一步【2】
diskCacheProvider.getDiskCache().put(resultKey, writer);
}
}
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
if (resource != null) {
if (resource.isCacheable()) {
//【2】即写入了弱引用 内存缓存
//下一步【3】
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
}
}
}
//【3】
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
// 步骤1:将缓存图片从弱引用缓存中移除
activeResources.remove(cacheKey);
if (resource.isCacheable()) {
// 步骤2:将该图片缓存放入Lru缓存
cache.put(cacheKey, resource);
}
}
}
细心的会发现我们上面使用的弱引用缓存,其实就是一个 hashMap 对象,只要图片加载成功就会放入这个 map 缓存,那么 map里的缓存对象是怎么维护的?
答案就是:
ReferenceQueue+IdleHandler
ReferenceQueue作用:
ReferenceQueue可翻译为引用队列,换言之就是存放引用的队列,保存的是Reference对象。
其作用在于Reference对象所引用的对象被GC回收时,该Reference对象将会被加入引用队列(ReferenceQueue)中
IdleHandler 原理:
略...
在消息队列MessageQueue空闲的时候,会执行IdleHandler,
清除ReferenceQueue中清空ResourceWeakReference对象
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
// 这是map 是一个强引用对象
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
private ReferenceQueue<EngineResource<?>> getReferenceQueue() {
if (resourceReferenceQueue == null) {
resourceReferenceQueue = new ReferenceQueue<EngineResource<?>>();
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue));
}
return resourceReferenceQueue;
}
//IdleHandler
private static class RefQueueIdleHandler implements MessageQueue.IdleHandler {
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
private final ReferenceQueue<EngineResource<?>> queue;
public RefQueueIdleHandler(Map<Key, WeakReference<EngineResource<?>>> activeResources,
ReferenceQueue<EngineResource<?>> queue) {
this.activeResources = activeResources;
this.queue = queue;
}
@Override
public boolean queueIdle() {
//从队列中取出一个元素,队列为空则返回null
//不会空说明ReferenceQueue有被GC回收的ResourceWeakReference对象.
ResourceWeakReference ref = (ResourceWeakReference) queue.poll();
if (ref != null) {
//清空 map 中 被GC回收的ResourceWeakReference对象
activeResources.remove(ref.key);
}
return true;
}
}
}
2个
一个线程池负责从本地获取图片数据
一个线程池负责从网络获取图片数据
class EngineJob implements EngineRunnable.EngineRunnableManager {
private final ExecutorService diskCacheService;
private final ExecutorService sourceService;
}
一张图片加载的流程
Glide 传入 url
【--UI线程->】从内存获取 ,成功则返回
【--本地线程池子线程->】从本地获取,转换的图片数据,成功则返回
【--本地线程池子线程->】从本地获取,原始图片数据,成功则返回
【--网络线程池子线程->】根据 url 网络加载,失败 显示 error 图片
【--网络线程池子线程->】若设置源数据存储 磁盘存储原始图片数据,然后解码, 否则跳过这一步
【--网络线程池子线程->】直接解码
【--网络线程池子线程->】解码图片数据转换,本地存储转换的图片数据,
(所谓转换就是图片处理,缩放,圆角等等操作),使用 handler 切换线程
【--UI线程->】 弱引用缓存存储图片数据
【--UI线程->】 图片显示,动画巴拉巴拉的执行...
IdleHandler 使用
见相关文章
更多内容 欢迎关注公众号
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。