文章将会被同步至微信公众号:Android部落格
一、BitmapMemoryCacheProducer
从第三篇文章中可以看到Producer的初始化顺序是BitmapMemoryCacheProducer->DecodeProducer,由此看到解码成功的图片还要经过内存缓存,等于是说光内存缓存就有两份,一份编码的,一份解码的。
这边文章讲解码之后的数据缓存。
经过DecodeProducer解码之后的数据,被包裹在CloseableStaticBitmap对象中,然后向上传递。
1. produceResults
//BitmapMemoryCacheProducer
@Override
public void produceResults(
final Consumer<CloseableReference<CloseableImage>> consumer, final ProducerContext producerContext) {
CloseableReference<CloseableImage> cachedReference = mMemoryCache.get(cacheKey);
if (cachedReference != null) {
consumer.onNewResult(cachedReference, BaseConsumer.simpleStatusForIsLast(isFinal));
cachedReference.close();
} else {
Consumer<CloseableReference<CloseableImage>> wrappedConsumer = wrapConsumer(
consumer, cacheKey, producerContext.getImageRequest().isMemoryCacheEnabled());
mInputProducer.produceResults(wrappedConsumer, producerContext);
}
}
先看看缓存里面有没有目标的数据,有的话通知其他consumer可以结束了,我这里已经取到数据了。当然目标数据要符合质量要求。
2. Consumer
当前缓存中找不到目标数据的话,就要设置消费者来拦截并处理数据了。消费者的定义在wrapConsumer方法中:
//BitmapMemoryCacheProducer
protected Consumer<CloseableReference<CloseableImage>> wrapConsumer(){
return new DelegatingConsumer<
CloseableReference<CloseableImage>, CloseableReference<CloseableImage>>(consumer) {
@Override
public void onNewResultImpl(
CloseableReference<CloseableImage> newResult, @Status int status) {
final boolean isLast = isLast(status);
if(newResult == null && isLast){
getConsumer().onNewResult(null, status);
return;
}
CloseableReference<CloseableImage> currentCachedResult = mMemoryCache.get(cacheKey);
if (currentCachedResult != null) {
getConsumer().onNewResult(currentCachedResult, status);
return;
}
CloseableReference<CloseableImage> newCachedResult = null;
if (isMemoryCacheEnabled) {
newCachedResult = mMemoryCache.cache(cacheKey, newResult);
getConsumer().onNewResult((newCachedResult != null) ? newCachedResult : newResult, status);
}
}
}
}
三个步骤:
- 状态检查,是结束标志且传过来的数据为null的话,不往下走了,直接往上传递null值;
- 检查当前缓存里面有没有当前key对应的value,有且符合质量的话,就返回当前内存里面的缓存数据;
- 前两个条件都不满足,就要将数据缓存了,这里缓存的逻辑与编码数据的缓存是一样的。
//CountingMemoryCache
public @Nullable CloseableReference<V> cache(
final K key, final CloseableReference<V> valueRef, final EntryStateObserver<K> observer) {
maybeUpdateCacheParams();
oldExclusive = mExclusiveEntries.remove(key);
Entry<K, V> oldEntry = mCachedEntries.remove(key);
if (oldEntry != null) {
makeOrphan(oldEntry);
oldRefToClose = referenceToClose(oldEntry);
}
if (canCacheNewValue(valueRef.get())) {
Entry<K, V> newEntry = Entry.of(key, valueRef, observer);
mCachedEntries.put(key, newEntry);
clientRef = newClientReference(newEntry);
}
maybeEvictEntries();
}
五个步骤:
- 检查当前的内存空间是否符合内存缓存的大小限制
- 删除待删除和已缓存map中存在key值对应的旧数据
- 将Entry的orphan属性置为1,并且检查是否可以关闭释放
- 此时再检查数据大小是否符合单个数据缓存大小的限制
再贴一下内存缓存的空间大小限制:
public class DefaultBitmapMemoryCacheParamsSupplier implements Supplier<MemoryCacheParams> {
private static final int MAX_CACHE_ENTRIES = 256;
private static final int MAX_EVICTION_QUEUE_SIZE = Integer.MAX_VALUE;
private static final int MAX_EVICTION_QUEUE_ENTRIES = Integer.MAX_VALUE;
private static final int MAX_CACHE_ENTRY_SIZE = Integer.MAX_VALUE;
private static final long PARAMS_CHECK_INTERVAL_MS = TimeUnit.MINUTES.toMillis(5);
private final ActivityManager mActivityManager;
public DefaultBitmapMemoryCacheParamsSupplier(ActivityManager activityManager) {
mActivityManager = activityManager;
}
@Override
public MemoryCacheParams get() {
return new MemoryCacheParams(
getMaxCacheSize(),
MAX_CACHE_ENTRIES,
MAX_EVICTION_QUEUE_SIZE,
MAX_EVICTION_QUEUE_ENTRIES,
MAX_CACHE_ENTRY_SIZE,
PARAMS_CHECK_INTERVAL_MS);
}
private int getMaxCacheSize() {
final int maxMemory =
Math.min(mActivityManager.getMemoryClass() * ByteConstants.MB, Integer.MAX_VALUE);
if (maxMemory < 32 * ByteConstants.MB) {
return 4 * ByteConstants.MB;
} else if (maxMemory < 64 * ByteConstants.MB) {
return 6 * ByteConstants.MB;
} else {
// We don't want to use more ashmem on Gingerbread for now, since it doesn't respond well to
// native memory pressure (doesn't throw exceptions, crashes app, crashes phone)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return 8 * ByteConstants.MB;
} else {
return maxMemory / 4;
}
}
}
}
getMemoryClass
Return the approximate per-application memory class of the current device. This gives you an idea of how hard a memory limit you should impose on your application to let the overall system work best. The returned value is in megabytes; the baseline Android memory class is 16 (which happens to be the Java heap limit of those devices); some devices with more memory may return 24 or even higher numbers.
- 符合条件,将CloseableReference
类型的数据put到mCachedEntries中,同时将clientCount属相加1,orphan置为false。 - 再次检查内存空间,看看是否有可以删除的数据,腾出缓存空间。
缓存成功,返回缓存之后的数据,并将数据上传个下一个消费者。