/* * 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 android.os.Build; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Executor; import com.facebook.common.internal.Closeables; import com.facebook.common.internal.Supplier; import com.facebook.common.references.CloseableReference; import com.facebook.common.util.ByteConstants; import com.facebook.imagepipeline.image.EncodedImage; import com.facebook.imagepipeline.memory.PooledByteBuffer; import com.facebook.imagepipeline.memory.PooledByteBufferFactory; import com.facebook.imagepipeline.request.ImageRequest; /** * Represents a local fetch producer. */ public abstract class LocalFetchProducer implements Producer<EncodedImage> { private final Executor mExecutor; private final PooledByteBufferFactory mPooledByteBufferFactory; private final boolean mDecodeFileDescriptorEnabledForKitKat; protected LocalFetchProducer( Executor executor, PooledByteBufferFactory pooledByteBufferFactory, boolean fileDescriptorEnabled) { mExecutor = executor; mPooledByteBufferFactory = pooledByteBufferFactory; mDecodeFileDescriptorEnabledForKitKat = fileDescriptorEnabled && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT; } @Override public void produceResults( final Consumer<EncodedImage> consumer, final ProducerContext producerContext) { final ProducerListener listener = producerContext.getListener(); final String requestId = producerContext.getId(); final ImageRequest imageRequest = producerContext.getImageRequest(); final StatefulProducerRunnable cancellableProducerRunnable = new StatefulProducerRunnable<EncodedImage>( consumer, listener, getProducerName(), requestId) { @Override protected EncodedImage getResult() throws Exception { EncodedImage encodedImage = getEncodedImage(imageRequest); if (encodedImage == null) { return null; } encodedImage.parseMetaData(); return encodedImage; } @Override protected void disposeResult(EncodedImage result) { EncodedImage.closeSafely(result); } }; producerContext.addCallbacks( new BaseProducerContextCallbacks() { @Override public void onCancellationRequested() { cancellableProducerRunnable.cancel(); } }); mExecutor.execute(cancellableProducerRunnable); } /** Creates a memory-backed encoded image from the stream. The stream is closed. */ protected EncodedImage getByteBufferBackedEncodedImage( InputStream inputStream, int length) throws IOException { CloseableReference<PooledByteBuffer> ref = null; try { if (length < 0) { ref = CloseableReference.of(mPooledByteBufferFactory.newByteBuffer(inputStream)); } else { ref = CloseableReference.of(mPooledByteBufferFactory.newByteBuffer(inputStream, length)); } return new EncodedImage(ref); } finally { Closeables.closeQuietly(inputStream); CloseableReference.closeSafely(ref); } } protected EncodedImage getEncodedImage( InputStream inputStream, int length) throws IOException { Runtime runTime = Runtime.getRuntime(); long javaMax = runTime.maxMemory(); long javaUsed = runTime.totalMemory() - runTime.freeMemory(); long javaFree = Math.min(javaMax - javaUsed, 8 * ByteConstants.MB); if (mDecodeFileDescriptorEnabledForKitKat && inputStream instanceof FileInputStream && javaMax >= 64 * javaFree) { return getInputStreamBackedEncodedImage(new File(inputStream.toString()), length); } else { return getByteBufferBackedEncodedImage(inputStream, length); } } protected EncodedImage getInputStreamBackedEncodedImage( final File file, int length) throws IOException { Supplier<FileInputStream> sup = new Supplier<FileInputStream>() { @Override public FileInputStream get() { try { return new FileInputStream(file); } catch (IOException ioe) { throw new RuntimeException(ioe); } } }; return new EncodedImage(sup, length); } /** * Gets an encoded image from the local resource. It can be either backed by a FileInputStream or * a PooledByteBuffer * @param imageRequest request that includes the local resource that is being accessed * @throws IOException */ protected abstract EncodedImage getEncodedImage(ImageRequest imageRequest) throws IOException; /** * @return name of the Producer */ protected abstract String getProducerName(); }