android glide缓存原理,Glide缓存机制

前言

本文基于Glide v3.7.0源码分析,Glide v4.0大致流程和v3.7.0差不多,在一些技术细节上有修改。主要内容有:

内存缓存读取

内存缓存写入

缓存引用计数

硬盘缓存读取

硬盘缓存写入

内存缓存读取

内存缓存相关代码主要在Engine.java中

public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,

DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder,

Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {

Util.assertMainThread();

long startTime = LogTime.getLogTime();

final String id = fetcher.getId();

EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),

loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),

transcoder, loadProvider.getSourceEncoder());

// 从LruCache中寻找缓存

EngineResource> cached = loadFromCache(key, isMemoryCacheable);

if (cached != null) {

cb.onResourceReady(cached);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Loaded resource from cache", startTime, key);

}

return null;

}

// LruCache中没有找到缓存,从弱引用中寻找缓存

EngineResource> active = loadFromActiveResources(key, isMemoryCacheable);

if (active != null) {

cb.onResourceReady(active);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

logWithTimeAndKey("Loaded resource from active resources", startTime, key);

}

return null;

}

...

}

两级内存缓存:先从LruCache中寻找,如果找到了缓存,将图片移出LruCache,加入activeResources弱引用缓存。如果在LruCache中没找到的话到activeResources弱引用缓存中寻找。如果在内存缓存中找到,则引用计数加1。使用中的图片用弱引用缓存来管理,没有使用的图片用LruCache来管理,判断图片有没有使用的依据之一是引用计数,当引用计数等于0时,将图片从弱引用缓存中移走,加入LruCache中。

内存缓存写入

图片会先写入到activeResources弱引用缓存中。

@Override

public void onEngineJobComplete(Key key, EngineResource> resource) {

Util.assertMainThread();

// A null resource indicates that the load failed, usually due to an exception.

if (resource != null) {

resource.setResourceListener(key, this);

if (resource.isCacheable()) {

activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));

}

}

jobs.remove(key);

}

当引用计数为0的时候会将图片放到LruCache中。

@Override

public void onResourceReleased(Key cacheKey, EngineResource resource) {

Util.assertMainThread();

activeResources.remove(cacheKey);

if (resource.isCacheable()) {

cache.put(cacheKey, resource);

} else {

resourceRecycler.recycle(resource);

}

}

缓存引用计数

这里其实就有个问题了,当我们滑动图片列表的时候,被回收的View中的图片自然也用不到了,我们并没有人为的去把计数器减1,那么Glide又是怎么知道图片已经没有被引用了,从而将它放到LruCache中的呢?其实Glide并不知道,但activeResources包含的值是图片资源的弱引用。

private final Map>> activeResources;

当滑动图片列表的时候,系统会根据需要将这些图片资源给回收掉,所以activeResources.get(key).get()得到的就会为null,为null也没有必要添加到LruCache中了。

但是这样子一来,activeResources中就会有很多没有用的item了,而它们又没有被移除掉。为了解决这个问题,Glide用了MessageQueue.IdleHandler这个利器来解决这个问题。

(IdleHandler也可以用来解决App启动延时加载的问题,具体可以看Android 启动优化之延时加载)

private static class RefQueueIdleHandler implements MessageQueue.IdleHandler {

private final Map>> activeResources;

private final ReferenceQueue> queue;

public RefQueueIdleHandler(Map>> activeResources,

ReferenceQueue> queue) {

this.activeResources = activeResources;

this.queue = queue;

}

@Override

public boolean queueIdle() {

ResourceWeakReference ref = (ResourceWeakReference) queue.poll();

if (ref != null) {

activeResources.remove(ref.key);

}

return true;

}

}

在Glide v4.0版本,Glide不是用IdleHandler来解决这个问题的了,而是开了一个线程优先级为Process.THREAD_PRIORITY_BACKGROUND的线程,然后再通过MainHandler发送消息到主线程中去处理的,技术实现不同,但目的是一样的。

硬盘缓存读取

当从两级内存缓存中都获取不到图片的时候,会开启线程,尝试从硬盘中获取缓存。根据硬盘缓存策略,可以只缓存转换过后的图片,也可以缓存原始图片。所以从硬盘中获取缓存的时候,会有两种方法,分别对应DiskCacheStrategy.RESULT和DiskCacheStrategy.SOURCE。相关代码在DecodeJob.java中。

private Resource> decodeFromCache() throws Exception {

Resource> result = null;

try {

// DiskCacheStrategy.RESULT

result = decodeJob.decodeResultFromCache();

} catch (Exception e) {

if (Log.isLoggable(TAG, Log.DEBUG)) {

Log.d(TAG, "Exception decoding result from cache: " + e);

}

}

if (result == null) {

// DiskCacheStrategy.SOURCE

result = decodeJob.decodeSourceFromCache();

}

return result;

}

硬盘缓存写入

在没有任何缓存的情况下,会解码原始图片

public Resource decodeFromSource() throws Exception {

// 解码原始数据

Resource decoded = decodeSource();

// 根据需要转换图片

return transformEncodeAndTranscode(decoded);

}

缓存原始图片:

private Resource decodeSource() throws Exception {

Resource decoded = null;

try {

long startTime = LogTime.getLogTime();

final A data = fetcher.loadData(priority);

if (isCancelled) {

return null;

}

decoded = decodeFromSourceData(data);

} finally {

fetcher.cleanup();

}

return decoded;

}

private Resource decodeFromSourceData(A data) throws IOException {

final Resource decoded;

if (diskCacheStrategy.cacheSource()) {

decoded = cacheAndDecodeSourceData(data);

} else {

long startTime = LogTime.getLogTime();

decoded = loadProvider.getSourceDecoder().decode(data, width, height);

}

return decoded;

}

private Resource cacheAndDecodeSourceData(A data) throws IOException {

long startTime = LogTime.getLogTime();

SourceWriter writer = new SourceWriter(loadProvider.getSourceEncoder(), data);

diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);

startTime = LogTime.getLogTime();

Resource result = loadFromCache(resultKey.getOriginalKey());

return result;

}

可以看到,开启了原始图片缓存的情况下,Glide选择将原始图片先写入硬盘缓存,然后再从硬盘缓存中加载图片。如果没有开启原始图片缓存,则直接解码原始数据。

缓存结果图片:

private Resource transformEncodeAndTranscode(Resource decoded) {

long startTime = LogTime.getLogTime();

// 先对图片进行转换

Resource transformed = transform(decoded);

// 缓存转换后的图片

writeTransformedToCache(transformed);

startTime = LogTime.getLogTime();

Resource result = transcode(transformed);

return result;

}

private void writeTransformedToCache(Resource transformed) {

if (transformed == null || !diskCacheStrategy.cacheResult()) {

return;

}

long startTime = LogTime.getLogTime();

SourceWriter> writer = new SourceWriter>(loadProvider.getEncoder(), transformed);

diskCacheProvider.getDiskCache().put(resultKey, writer);

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值