/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.imagepipeline.producers; import com.facebook.common.internal.ImmutableMap; import com.facebook.common.references.CloseableReference; import com.facebook.imagepipeline.cache.MemoryCache; import com.facebook.imagepipeline.cache.CacheKeyFactory; import com.facebook.imagepipeline.image.EncodedImage; import com.facebook.imagepipeline.memory.PooledByteBuffer; import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.cache.common.CacheKey; import com.facebook.common.internal.VisibleForTesting; /** * Memory cache producer for the encoded memory cache. */ public class EncodedMemoryCacheProducer implements Producer<EncodedImage> { @VisibleForTesting static final String PRODUCER_NAME = "EncodedMemoryCacheProducer"; @VisibleForTesting static final String VALUE_FOUND = "cached_value_found"; private final MemoryCache<CacheKey, PooledByteBuffer> mMemoryCache; private final CacheKeyFactory mCacheKeyFactory; private final Producer<EncodedImage> mInputProducer; public EncodedMemoryCacheProducer( MemoryCache<CacheKey, PooledByteBuffer> memoryCache, CacheKeyFactory cacheKeyFactory, Producer<EncodedImage> inputProducer) { mMemoryCache = memoryCache; mCacheKeyFactory = cacheKeyFactory; mInputProducer = inputProducer; } @Override public void produceResults( final Consumer<EncodedImage> consumer, final ProducerContext producerContext) { final String requestId = producerContext.getId(); final ProducerListener listener = producerContext.getListener(); listener.onProducerStart(requestId, PRODUCER_NAME); final ImageRequest imageRequest = producerContext.getImageRequest(); final CacheKey cacheKey = mCacheKeyFactory.getEncodedCacheKey(imageRequest); CloseableReference<PooledByteBuffer> cachedReference = mMemoryCache.get(cacheKey); try { if (cachedReference != null) { EncodedImage cachedEncodedImage = new EncodedImage(cachedReference); try { listener.onProducerFinishWithSuccess( requestId, PRODUCER_NAME, listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "true") : null); consumer.onProgressUpdate(1f); consumer.onNewResult(cachedEncodedImage, true); return; } finally { EncodedImage.closeSafely(cachedEncodedImage); } } if (producerContext.getLowestPermittedRequestLevel().getValue() >= ImageRequest.RequestLevel.ENCODED_MEMORY_CACHE.getValue()) { listener.onProducerFinishWithSuccess( requestId, PRODUCER_NAME, listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "false") : null); consumer.onNewResult(null, true); return; } Consumer<EncodedImage> consumerOfInputProducer = new DelegatingConsumer< EncodedImage, EncodedImage>(consumer) { @Override public void onNewResultImpl(EncodedImage newResult, boolean isLast) { // intermediate or null results are not cached, so we just forward them if (!isLast || newResult == null) { getConsumer().onNewResult(newResult, isLast); return; } // cache and forward the last result CloseableReference<PooledByteBuffer> ref = newResult.getByteBufferRef(); if (ref != null) { CloseableReference<PooledByteBuffer> cachedResult; try { cachedResult = mMemoryCache.cache(cacheKey, ref); } finally { CloseableReference.closeSafely(ref); } if (cachedResult != null) { EncodedImage cachedEncodedImage; try { cachedEncodedImage = new EncodedImage(cachedResult); cachedEncodedImage.copyMetaDataFrom(newResult); } finally { CloseableReference.closeSafely(cachedResult); } try { getConsumer().onProgressUpdate(1f); getConsumer().onNewResult(cachedEncodedImage, true); return; } finally { EncodedImage.closeSafely(cachedEncodedImage); } } } getConsumer().onNewResult(newResult, true); } }; listener.onProducerFinishWithSuccess( requestId, PRODUCER_NAME, listener.requiresExtraMap(requestId) ? ImmutableMap.of(VALUE_FOUND, "false") : null); mInputProducer.produceResults(consumerOfInputProducer, producerContext); } finally { CloseableReference.closeSafely(cachedReference); } } }