概述
前言
一、简介
缓存设置
Glide.with(this).load("url").skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.ALL)
设置内存缓存开关:true 表示不使用内存缓存
skipMemoryCache()
设置磁盘缓存模式:
diskCacheStrategy()
可以设置4种模式:
- DiskCacheStrategy.NONE:表示不缓存任何内容。
- DiskCacheStrategy.DATA:表示只缓存原始图片。
- DiskCacheStrategy.RESOURCE:表示只缓存转换过后的图片(默认选项)。
- DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。
这里需要主要是RESOURCE,当我们下载下来图片之后可能对图片裁剪,放缩等操作,所有转换之后的图片就是RESOURCE。
二 内存缓存
Engine.java
@Nullable
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
在下载图片之前会现在内存里面查找对应的图片,这个方法在Engine的loadFromMemory,
loadFromMemory 里面有分为两个部分一是ActiveResources,表示当前正在使用的资源,什么是当前正在使用?就是有某一个View 正在显示这个图片,根本上说是这个图片存在一个强引用。ActiveResources 使用的弱引用来缓存图片,弱引用应用的资源在没有强引用的时候会被自动回收。换句话说当页面销毁了图片的强引用就没了,此时系统会自动回收所有的弱引用引用的资源。这样可以更好的避免内存泄露。
loadFromCache 使用的Lru 缓存的图片,简单区分就是loadFromMemory 缓存的图片表示当前一定有一个View 在显示它,loadFromCache 厘米缓存的图片不一定存在View 显示它。
2.1 弱引用缓存
private final ActiveResources activeResources;
@Nullable
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
如果还有对WeakReference 不理解的同学可以百度一下。
2.2 LruCache
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
每次从LruCache 读取的缓存都要添加到ActiveResources里面,因为读取就表示需要显示。
private final MemoryCache cache;
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result =
new EngineResource<>(
cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
这里主要是通过cache.获取缓存的图片。
LruCache
Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
@Nullable
public synchronized Y remove(@NonNull T key) {
final Y value = cache.remove(key);
if (value != null) {
currentSize -= getSize(value);
}
return value;
}
此处的cache 是一个LinkedHashMap,LinkedHashMap 本身实现了Lru算法,有不了解的同学可以自行查询,这里不做介绍。
三 磁盘缓存
在Glide中磁盘缓存默认使用的也是LRU(Least Recently Used)算法。如果你想使用其他的缓存算法,就只能通过实现DiskCache接口来完成了。
在创建Glide 的时候会使用一个默认的DiskLruCacheFactory 来创建对应的DiskLruCache
InternalCacheDiskCacheFactory
public static DiskCache create(File directory, long maxSize) {
return new DiskLruCacheWrapper(directory, maxSize);
}
最终创建的对象是DiskLruCacheWrapper
protected DiskLruCacheWrapper(File directory, long maxSize) {
//缓存的目录
this.directory = directory;
//缓存的大小
this.maxSize = maxSize;
this.safeKeyGenerator = new SafeKeyGenerator();
}
private synchronized DiskLruCache getDiskCache() throws IOException {
if (diskLruCache == null) {
diskLruCache = DiskLruCache.open(directory, APP_VERSION,
VALUE_COUNT, maxSize);
}
return diskLruCache;
}
@Override
public File get(Key key) {
//获取文件的名字
String safeKey = safeKeyGenerator.getSafeKey(key);
File result = null;
try {
final DiskLruCache.Value value = getDiskCache().get(safeKey);
if (value != null) {
//获取对应的文件
result = value.getFile(0);
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to get from disk cache", e);
}
}
return result;
}
@Override
public void put(Key key, Writer writer) {
//生成key
String safeKey = safeKeyGenerator.getSafeKey(key);
//获取锁
writeLocker.acquire(safeKey);
try {
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
throw new IllegalStateException(
"Had two simultaneous puts for: " + safeKey);
}
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
}
} finally {
//释放锁
writeLocker.release(safeKey);
}
}
@Override
public void delete(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
try {
getDiskCache().remove(safeKey);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to delete from disk cache", e);
}
}
}
}
这里在参数key的基础上再调用了safeKeyGenerator.getSafeKey()生成safeKey 这个safeKey 就是文件的名字,然后采用了Lock机制,获取锁,进行缓存操作,在finally中释放锁。
这里对文件的缓存使用的也是DiskLruCache,DiskLruCache 是一个有名的磁盘缓存矿建,在OkHttp 里面也使用到了这个框架。有不了解这个框架的可以查阅一下相关资料也可以看我之前写的一篇文章。
获取获取的调用时机我们已经知道,下面我们看看保存缓存文件的时机。
3.1 缓存图片原文件
前面介绍过Glide 通过SourceGenerator下载图片,当图片下载之后会调用cacheData
@Override
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
//缓存文件
helper.getDiskCache().put(originalKey, writer);
} finally {
loadData.fetcher.cleanup();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
这里的dataToCache是个InputStream,其中调用helper.getSourceEncoder(dataToCache)获取Encoder对象,
<X> Encoder<X> getSourceEncoder(X data) throws Registry.NoSourceEncoderAvailableException {
return glideContext.getRegistry().getSourceEncoder(data);
}
获取一个Encoder将 InputStream转换为图片,这里实际对应的是StreamEncoder
得到Encoder解码器后,包装为一个DataCacheWriter,最后调用put方法进行缓存。
我们看到DiskLruCacheWrapper的put方法
public void put(Key key, Writer writer) {
//生成文件名
String safeKey = safeKeyGenerator.getSafeKey(key);
writeLocker.acquire(safeKey);
try {
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
}
try {
File file = editor.getFile(0);
.//保存文件
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
}
} finally {
writeLocker.release(safeKey);
}
}
这里主要是通过writer.write(file) 来保存对应的文件,writer是DataCacheWriter
class DataCacheWriter<DataType> implements DiskCache.Writer {
private final Encoder<DataType> encoder;
private final DataType data;
private final Options options;
DataCacheWriter(Encoder<DataType> encoder, DataType data, Options options) {
this.encoder = encoder;
this.data = data;
this.options = options;
}
@Override
public boolean write(@NonNull File file) {
return encoder.encode(data, file, options);
}
}
encoder 的实际为StreamEncoder。
@Override
public boolean encode(@NonNull InputStream data, @NonNull File file, @NonNull Options options) {
byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
boolean success = false;
OutputStream os = null;
try {
os = new FileOutputStream(file);
int read;
while ((read = data.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
os.close();
success = true;
} catch (IOException e) {
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Do nothing.
}
}
byteArrayPool.put(buffer);
}
return success;
}
}
很简单,就是文件的读写而已~~
以上介绍的是缓存的图片源文件,下面我们看看对图片转换后的缓存
3.2 缓存转换后的图片
这一部分在DecodeJob 里面,当下载图片并执行完图片转换之后会执行到DecodeJob 的decodeFromRetrievedData 方法
decodeFromRetrievedData 最终归会调用到DeferredEncodeManager 的encode 方法
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
通过DiskCache 的put方法缓存图片,put 方法在上面有源码。
这里的encoder 一般是BitmapDrawableDecoder
public boolean encode(
@NonNull Resource<BitmapDrawable> data, @NonNull File file, @NonNull Options options) {
return encoder.encode(new BitmapResource(data.get().getBitmap(), bitmapPool), file, options);
}
``
BitmapDrawableEncoder 调用了BitmapEncoder`的encode
```java
@Override
public boolean encode(
@NonNull Resource<Bitmap> resource, @NonNull File file, @NonNull Options options) {
final Bitmap bitmap = resource.get();
Bitmap.CompressFormat format = getFormat(bitmap, options);
try {
long start = LogTime.getLogTime();
int quality = options.get(COMPRESSION_QUALITY);
boolean success = false;
OutputStream os = null;
try {
os = new FileOutputStream(file);
if (arrayPool != null) {
os = new BufferedOutputStream(os, arrayPool);
}
//将图片保存的文件里面去
bitmap.compress(format, quality, os);
os.close();
success = true;
} catch (IOException e) {
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Do nothing.
}
}
}
}
return success;
} finally {
GlideTrace.endSection();
}
}
可以看到最终通过 bitmap.compress(format, quality, os); 将图片保存到对应的文件里面去
最后
以上就是舒心大树为你收集整理的Android 开发之Glide《五》缓存前言一、简介二 内存缓存三 磁盘缓存的全部内容,希望文章能够帮你解决Android 开发之Glide《五》缓存前言一、简介二 内存缓存三 磁盘缓存所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复