/*
* 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.core;
import javax.annotation.concurrent.NotThreadSafe;
import android.content.Context;
import android.os.Build;
import android.support.v4.util.Pools;
import com.facebook.cache.common.CacheKey;
import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.cache.disk.FileCache;
import com.facebook.common.internal.AndroidPredicates;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.Suppliers;
import com.facebook.common.memory.PooledByteBuffer;
import com.facebook.imageformat.ImageFormatChecker;
import com.facebook.imagepipeline.animated.factory.AnimatedFactory;
import com.facebook.imagepipeline.animated.factory.AnimatedFactoryProvider;
import com.facebook.imagepipeline.animated.factory.AnimatedImageFactory;
import com.facebook.imagepipeline.bitmaps.ArtBitmapFactory;
import com.facebook.imagepipeline.bitmaps.EmptyJpegGenerator;
import com.facebook.imagepipeline.bitmaps.GingerbreadBitmapFactory;
import com.facebook.imagepipeline.bitmaps.HoneycombBitmapFactory;
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory;
import com.facebook.imagepipeline.cache.BitmapCountingMemoryCacheFactory;
import com.facebook.imagepipeline.cache.BitmapMemoryCacheFactory;
import com.facebook.imagepipeline.cache.BufferedDiskCache;
import com.facebook.imagepipeline.cache.CountingMemoryCache;
import com.facebook.imagepipeline.cache.EncodedCountingMemoryCacheFactory;
import com.facebook.imagepipeline.cache.EncodedMemoryCacheFactory;
import com.facebook.imagepipeline.cache.MediaVariationsIndex;
import com.facebook.imagepipeline.cache.MediaVariationsIndexDatabase;
import com.facebook.imagepipeline.cache.MemoryCache;
import com.facebook.imagepipeline.cache.NoOpMediaVariationsIndex;
import com.facebook.imagepipeline.decoder.DefaultImageDecoder;
import com.facebook.imagepipeline.decoder.ImageDecoder;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.memory.PoolFactory;
import com.facebook.imagepipeline.platform.ArtDecoder;
import com.facebook.imagepipeline.platform.GingerbreadPurgeableDecoder;
import com.facebook.imagepipeline.platform.KitKatPurgeableDecoder;
import com.facebook.imagepipeline.platform.PlatformDecoder;
import com.facebook.imagepipeline.producers.ThreadHandoffProducerQueue;
/**
* Factory class for the image pipeline.
*
* <p>This class constructs the pipeline and its dependencies from other libraries.
*
* <p>As the pipeline object can be quite expensive to create, it is strongly
* recommended that applications create just one instance of this class
* and of the pipeline.
*/
@NotThreadSafe
public class ImagePipelineFactory {
private static ImagePipelineFactory sInstance = null;
private final ThreadHandoffProducerQueue mThreadHandoffProducerQueue;
/**
* Gets the instance of {@link ImagePipelineFactory}.
*/
public static ImagePipelineFactory getInstance() {
return Preconditions.checkNotNull(sInstance, "ImagePipelineFactory was not initialized!");
}
/**
* Initializes {@link ImagePipelineFactory} with default config.
*/
public static void initialize(Context context) {
initialize(ImagePipelineConfig.newBuilder(context).build());
}
/**
* Initializes {@link ImagePipelineFactory} with the specified config.
*/
public static void initialize(ImagePipelineConfig imagePipelineConfig) {
sInstance = new ImagePipelineFactory(imagePipelineConfig);
}
/**
* Shuts {@link ImagePipelineFactory} down.
*/
public static void shutDown() {
if (sInstance != null) {
sInstance.getBitmapMemoryCache().removeAll(AndroidPredicates.<CacheKey>True());
sInstance.getEncodedMemoryCache().removeAll(AndroidPredicates.<CacheKey>True());
sInstance = null;
}
}
private final ImagePipelineConfig mConfig;
private CountingMemoryCache<CacheKey, CloseableImage>
mBitmapCountingMemoryCache;
private MemoryCache<CacheKey, CloseableImage> mBitmapMemoryCache;
private CountingMemoryCache<CacheKey, PooledByteBuffer> mEncodedCountingMemoryCache;
private MemoryCache<CacheKey, PooledByteBuffer> mEncodedMemoryCache;
private BufferedDiskCache mMainBufferedDiskCache;
private FileCache mMainFileCache;
private ImageDecoder mImageDecoder;
private ImagePipeline mImagePipeline;
private ProducerFactory mProducerFactory;
private ProducerSequenceFactory mProducerSequenceFactory;
private BufferedDiskCache mSmallImageBufferedDiskCache;
private FileCache mSmallImageFileCache;
private MediaVariationsIndex mMediaVariationsIndex;
private PlatformBitmapFactory mPlatformBitmapFactory;
private PlatformDecoder mPlatformDecoder;
private AnimatedFactory mAnimatedFactory;
public ImagePipelineFactory(ImagePipelineConfig config) {
mConfig = Preconditions.checkNotNull(config);
mThreadHandoffProducerQueue = new ThreadHandoffProducerQueue(
config.getExecutorSupplier().forLightweightBackgroundTasks());
}
public AnimatedFactory getAnimatedFactory() {
if (mAnimatedFactory == null) {
mAnimatedFactory = AnimatedFactoryProvider.getAnimatedFactory(
getPlatformBitmapFactory(),
mConfig.getExecutorSupplier());
}
return mAnimatedFactory;
}
public CountingMemoryCache<CacheKey, CloseableImage>
getBitmapCountingMemoryCache() {
if (mBitmapCountingMemoryCache == null) {
mBitmapCountingMemoryCache =
BitmapCountingMemoryCacheFactory.get(
mConfig.getBitmapMemoryCacheParamsSupplier(),
mConfig.getMemoryTrimmableRegistry(),
getPlatformBitmapFactory(),
mConfig.getExperiments().isExternalCreatedBitmapLogEnabled(),
mConfig.getBitmapMemoryCacheTrimStrategy());
}
return mBitmapCountingMemoryCache;
}
public MemoryCache<CacheKey, CloseableImage> getBitmapMemoryCache() {
if (mBitmapMemoryCache == null) {
mBitmapMemoryCache =
BitmapMemoryCacheFactory.get(
getBitmapCountingMemoryCache(),
mConfig.getImageCacheStatsTracker());
}
return mBitmapMemoryCache;
}
public CountingMemoryCache<CacheKey, PooledByteBuffer> getEncodedCountingMemoryCache() {
if (mEncodedCountingMemoryCache == null) {
mEncodedCountingMemoryCache =
EncodedCountingMemoryCacheFactory.get(
mConfig.getEncodedMemoryCacheParamsSupplier(),
mConfig.getMemoryTrimmableRegistry(),
getPlatformBitmapFactory());
}
return mEncodedCountingMemoryCache;
}
public MemoryCache<CacheKey, PooledByteBuffer> getEncodedMemoryCache() {
if (mEncodedMemoryCache == null) {
mEncodedMemoryCache =
EncodedMemoryCacheFactory.get(
getEncodedCountingMemoryCache(),
mConfig.getImageCacheStatsTracker());
}
return mEncodedMemoryCache;
}
private ImageDecoder getImageDecoder() {
if (mImageDecoder == null) {
if (mConfig.getImageDecoder() != null) {
mImageDecoder = mConfig.getImageDecoder();
} else {
final AnimatedFactory animatedFactory = getAnimatedFactory();
final AnimatedImageFactory animatedImageFactory;
if (animatedFactory != null) {
animatedImageFactory = getAnimatedFactory().getAnimatedImageFactory();
} else {
animatedImageFactory = null;
}
if (mConfig.getImageDecoderConfig() == null) {
mImageDecoder = new DefaultImageDecoder(
animatedImageFactory,
getPlatformDecoder(),
mConfig.getBitmapConfig());
} else {
mImageDecoder = new DefaultImageDecoder(
animatedImageFactory,
getPlatformDecoder(),
mConfig.getBitmapConfig(),
mConfig.getImageDecoderConfig().getCustomImageDecoders());
// Add custom image formats if needed
ImageFormatChecker.getInstance()
.setCustomImageFormatCheckers(
mConfig.getImageDecoderConfig().getCustomImageFormats());
}
}
}
return mImageDecoder;
}
public BufferedDiskCache getMainBufferedDiskCache() {
if (mMainBufferedDiskCache == null) {
mMainBufferedDiskCache =
new BufferedDiskCache(
getMainFileCache(),
mConfig.getPoolFactory().getPooledByteBufferFactory(),
mConfig.getPoolFactory().getPooledByteStreams(),
mConfig.getExecutorSupplier().forLocalStorageRead(),
mConfig.getExecutorSupplier().forLocalStorageWrite(),
mConfig.getImageCacheStatsTracker());
}
return mMainBufferedDiskCache;
}
public FileCache getMainFileCache() {
if (mMainFileCache == null) {
DiskCacheConfig diskCacheConfig = mConfig.getMainDiskCacheConfig();
mMainFileCache = mConfig.getFileCacheFactory().get(diskCacheConfig);
}
return mMainFileCache;
}
public ImagePipeline getImagePipeline() {
if (mImagePipeline == null) {
mImagePipeline =
new ImagePipeline(
getProducerSequenceFactory(),
mConfig.getRequestListeners(),
mConfig.getIsPrefetchEnabledSupplier(),
getBitmapMemoryCache(),
getEncodedMemoryCache(),
getMainBufferedDiskCache(),
getSmallImageBufferedDiskCache(),
mConfig.getCacheKeyFactory(),
mThreadHandoffProducerQueue,
Suppliers.of(false));
}
return mImagePipeline;
}
/**
* Provide the implementation of the PlatformBitmapFactory for the current platform
* using the provided PoolFactory
*
* @param poolFactory The PoolFactory
* @param platformDecoder The PlatformDecoder
* @return The PlatformBitmapFactory implementation
*/
public static PlatformBitmapFactory buildPlatformBitmapFactory(
PoolFactory poolFactory,
PlatformDecoder platformDecoder) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return new ArtBitmapFactory(poolFactory.getBitmapPool());
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return new HoneycombBitmapFactory(
new EmptyJpegGenerator(poolFactory.getPooledByteBufferFactory()),
platformDecoder);
} else {
return new GingerbreadBitmapFactory();
}
}
public PlatformBitmapFactory getPlatformBitmapFactory() {
if (mPlatformBitmapFactory == null) {
mPlatformBitmapFactory = buildPlatformBitmapFactory(
mConfig.getPoolFactory(),
getPlatformDecoder());
}
return mPlatformBitmapFactory;
}
/**
* Provide the implementation of the PlatformDecoder for the current platform using the
* provided PoolFactory
*
* @param poolFactory The PoolFactory
* @return The PlatformDecoder implementation
*/
public static PlatformDecoder buildPlatformDecoder(
PoolFactory poolFactory,
boolean directWebpDirectDecodingEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int maxNumThreads = poolFactory.getFlexByteArrayPoolMaxNumThreads();
return new ArtDecoder(
poolFactory.getBitmapPool(),
maxNumThreads,
new Pools.SynchronizedPool<>(maxNumThreads));
} else {
if (directWebpDirectDecodingEnabled
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return new GingerbreadPurgeableDecoder();
} else {
return new KitKatPurgeableDecoder(poolFactory.getFlexByteArrayPool());
}
}
}
public PlatformDecoder getPlatformDecoder() {
if (mPlatformDecoder == null) {
mPlatformDecoder = buildPlatformDecoder(
mConfig.getPoolFactory(),
mConfig.getExperiments().isWebpSupportEnabled());
}
return mPlatformDecoder;
}
private ProducerFactory getProducerFactory() {
if (mProducerFactory == null) {
mProducerFactory =
new ProducerFactory(
mConfig.getContext(),
mConfig.getPoolFactory().getSmallByteArrayPool(),
getImageDecoder(),
mConfig.getProgressiveJpegConfig(),
mConfig.isDownsampleEnabled(),
mConfig.isResizeAndRotateEnabledForNetwork(),
mConfig.getExperiments().isDecodeCancellationEnabled(),
mConfig.getExecutorSupplier(),
mConfig.getPoolFactory().getPooledByteBufferFactory(),
getBitmapMemoryCache(),
getEncodedMemoryCache(),
getMainBufferedDiskCache(),
getSmallImageBufferedDiskCache(),
getMediaVariationsIndex(),
mConfig.getExperiments().getMediaIdExtractor(),
mConfig.getCacheKeyFactory(),
getPlatformBitmapFactory());
}
return mProducerFactory;
}
private ProducerSequenceFactory getProducerSequenceFactory() {
if (mProducerSequenceFactory == null) {
mProducerSequenceFactory =
new ProducerSequenceFactory(
getProducerFactory(),
mConfig.getNetworkFetcher(),
mConfig.isResizeAndRotateEnabledForNetwork(),
mConfig.getExperiments().isWebpSupportEnabled(),
mThreadHandoffProducerQueue,
mConfig.getExperiments().getUseDownsamplingRatioForResizing());
}
return mProducerSequenceFactory;
}
public FileCache getSmallImageFileCache() {
if (mSmallImageFileCache == null) {
DiskCacheConfig diskCacheConfig = mConfig.getSmallImageDiskCacheConfig();
mSmallImageFileCache = mConfig.getFileCacheFactory().get(diskCacheConfig);
}
return mSmallImageFileCache;
}
private BufferedDiskCache getSmallImageBufferedDiskCache() {
if (mSmallImageBufferedDiskCache == null) {
mSmallImageBufferedDiskCache =
new BufferedDiskCache(
getSmallImageFileCache(),
mConfig.getPoolFactory().getPooledByteBufferFactory(),
mConfig.getPoolFactory().getPooledByteStreams(),
mConfig.getExecutorSupplier().forLocalStorageRead(),
mConfig.getExecutorSupplier().forLocalStorageWrite(),
mConfig.getImageCacheStatsTracker());
}
return mSmallImageBufferedDiskCache;
}
public MediaVariationsIndex getMediaVariationsIndex() {
if (mMediaVariationsIndex == null) {
mMediaVariationsIndex = mConfig.getExperiments().getMediaVariationsIndexEnabled()
? new MediaVariationsIndexDatabase(
mConfig.getContext(),
mConfig.getExecutorSupplier().forLocalStorageRead(),
mConfig.getExecutorSupplier().forLocalStorageWrite())
: new NoOpMediaVariationsIndex();
}
return mMediaVariationsIndex;
}
}