From 60adb4effedd19547ea21b1ce866514581963170 Mon Sep 17 00:00:00 2001 From: Colin White Date: Mon, 8 Feb 2021 11:47:20 -0600 Subject: [PATCH] Add support for ImageLoader.newBuilder(). (#653) * Support ImageLoader.newBuilder and sharing memory caches between image loaders. * Add tests. * Fix imports. * Docs. --- coil-base/api/coil-base.api | 2 + .../java/coil/RealImageLoaderTest.kt | 43 ++++- .../java/coil/util/SystemCallbacksTest.kt | 11 +- coil-base/src/main/java/coil/ImageLoader.kt | 169 ++++++++++++------ .../src/main/java/coil/RealImageLoader.kt | 47 +++-- .../main/java/coil/memory/RealMemoryCache.kt | 8 +- .../main/java/coil/util/ImageLoaderOptions.kt | 14 ++ .../java/coil/bitmap/RealBitmapPoolTest.kt | 2 +- .../java/coil/memory/RealMemoryCacheTest.kt | 7 +- 9 files changed, 206 insertions(+), 97 deletions(-) create mode 100644 coil-base/src/main/java/coil/util/ImageLoaderOptions.kt diff --git a/coil-base/api/coil-base.api b/coil-base/api/coil-base.api index 8c3c8b785d..44ed529de6 100644 --- a/coil-base/api/coil-base.api +++ b/coil-base/api/coil-base.api @@ -76,6 +76,7 @@ public abstract interface class coil/ImageLoader { public abstract fun getBitmapPool ()Lcoil/bitmap/BitmapPool; public abstract fun getDefaults ()Lcoil/request/DefaultRequestOptions; public abstract fun getMemoryCache ()Lcoil/memory/MemoryCache; + public abstract fun newBuilder ()Lcoil/ImageLoader$Builder; public abstract fun shutdown ()V } @@ -105,6 +106,7 @@ public final class coil/ImageLoader$Builder { public final fun fallback (Landroid/graphics/drawable/Drawable;)Lcoil/ImageLoader$Builder; public final fun launchInterceptorChainOnMainThread (Z)Lcoil/ImageLoader$Builder; public final fun logger (Lcoil/util/Logger;)Lcoil/ImageLoader$Builder; + public final fun memoryCache (Lcoil/memory/MemoryCache;)Lcoil/ImageLoader$Builder; public final fun memoryCachePolicy (Lcoil/request/CachePolicy;)Lcoil/ImageLoader$Builder; public final fun networkCachePolicy (Lcoil/request/CachePolicy;)Lcoil/ImageLoader$Builder; public final fun okHttpClient (Lkotlin/jvm/functions/Function0;)Lcoil/ImageLoader$Builder; diff --git a/coil-base/src/androidTest/java/coil/RealImageLoaderTest.kt b/coil-base/src/androidTest/java/coil/RealImageLoaderTest.kt index 89fe563325..8ead6b28de 100644 --- a/coil-base/src/androidTest/java/coil/RealImageLoaderTest.kt +++ b/coil-base/src/androidTest/java/coil/RealImageLoaderTest.kt @@ -11,6 +11,7 @@ import android.graphics.Color import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.widget.ImageView +import androidx.core.graphics.createBitmap import androidx.core.net.toUri import androidx.lifecycle.Lifecycle import androidx.test.core.app.ApplicationProvider @@ -24,6 +25,7 @@ import coil.decode.Decoder import coil.decode.Options import coil.fetch.AssetUriFetcher.Companion.ASSET_FILE_PATH_ROOT import coil.memory.MemoryCache +import coil.memory.RealMemoryCache import coil.memory.RealWeakMemoryCache import coil.memory.StrongMemoryCache import coil.request.CachePolicy @@ -35,6 +37,7 @@ import coil.request.SuccessResult import coil.size.PixelSize import coil.size.Precision import coil.size.Size +import coil.util.ImageLoaderOptions import coil.util.TestActivity import coil.util.Utils import coil.util.activity @@ -65,6 +68,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlin.test.assertSame import kotlin.test.assertTrue class RealImageLoaderTest { @@ -85,18 +89,16 @@ class RealImageLoaderTest { val weakMemoryCache = RealWeakMemoryCache(null) val referenceCounter = RealBitmapReferenceCounter(weakMemoryCache, bitmapPool, null) strongMemoryCache = StrongMemoryCache(weakMemoryCache, referenceCounter, Int.MAX_VALUE, null) + val memoryCache = RealMemoryCache(strongMemoryCache, weakMemoryCache, referenceCounter, bitmapPool) imageLoader = RealImageLoader( context = context, defaults = DefaultRequestOptions(), bitmapPool = bitmapPool, - referenceCounter = referenceCounter, - strongMemoryCache = strongMemoryCache, - weakMemoryCache = weakMemoryCache, + memoryCache = memoryCache, callFactory = OkHttpClient(), eventListenerFactory = EventListener.Factory.NONE, componentRegistry = ComponentRegistry(), - addLastModifiedToFileCacheKey = true, - launchInterceptorChainOnMainThread = true, + options = ImageLoaderOptions(), logger = null ) activityRule.scenario.moveToState(Lifecycle.State.RESUMED) @@ -427,6 +429,37 @@ class RealImageLoaderTest { assertTrue(isSuccessful) } + @Test + fun newBuilderSharesMemoryCache() { + val key = MemoryCache.Key("fake_key") + val imageLoader1 = ImageLoader(context) + val imageLoader2 = imageLoader1.newBuilder().build() + + assertSame(imageLoader1.memoryCache, imageLoader2.memoryCache) + assertNull(imageLoader1.memoryCache[key]) + assertNull(imageLoader2.memoryCache[key]) + + val bitmap = createBitmap(100, 100) + imageLoader1.memoryCache[key] = bitmap + + assertSame(bitmap, imageLoader2.memoryCache[key]) + } + + @Test + fun newBuilderSharesBitmapPool() { + val imageLoader1 = ImageLoader.Builder(context).bitmapPoolPercentage(0.5).build() + val imageLoader2 = imageLoader1.newBuilder().build() + + assertSame(imageLoader1.bitmapPool, imageLoader2.bitmapPool) + assertNull(imageLoader1.bitmapPool.getOrNull(100, 100, Bitmap.Config.ARGB_8888)) + assertNull(imageLoader2.bitmapPool.getOrNull(100, 100, Bitmap.Config.ARGB_8888)) + + val bitmap = createBitmap(100, 100) + imageLoader1.bitmapPool.put(bitmap) + + assertSame(bitmap, imageLoader2.bitmapPool.getOrNull(100, 100, Bitmap.Config.ARGB_8888)) + } + private fun testEnqueue(data: Any, expectedSize: PixelSize = PixelSize(80, 100)) { val imageView = activityRule.scenario.activity.imageView imageView.scaleType = ImageView.ScaleType.FIT_CENTER diff --git a/coil-base/src/androidTest/java/coil/util/SystemCallbacksTest.kt b/coil-base/src/androidTest/java/coil/util/SystemCallbacksTest.kt index c1769312fe..108b59f981 100644 --- a/coil-base/src/androidTest/java/coil/util/SystemCallbacksTest.kt +++ b/coil-base/src/androidTest/java/coil/util/SystemCallbacksTest.kt @@ -13,6 +13,7 @@ import coil.RealImageLoader import coil.bitmap.BitmapPool import coil.bitmap.RealBitmapReferenceCounter import coil.memory.MemoryCache +import coil.memory.RealMemoryCache import coil.memory.RealWeakMemoryCache import coil.memory.StrongMemoryCache import coil.request.DefaultRequestOptions @@ -40,7 +41,7 @@ class SystemCallbacksTest { Runtime.getRuntime().gc() // Keep allocating bitmaps until either the image loader is freed or we run out of memory. - bitmaps += createBitmap(500, 500, Bitmap.Config.ARGB_8888) + bitmaps += createBitmap(500, 500) } bitmaps.clear() @@ -56,18 +57,16 @@ class SystemCallbacksTest { val weakMemoryCache = RealWeakMemoryCache(null) val referenceCounter = RealBitmapReferenceCounter(weakMemoryCache, bitmapPool, null) val strongMemoryCache = StrongMemoryCache(weakMemoryCache, referenceCounter, Int.MAX_VALUE, null) + val memoryCache = RealMemoryCache(strongMemoryCache, weakMemoryCache, referenceCounter, bitmapPool) val imageLoader = RealImageLoader( context = context, defaults = DefaultRequestOptions(), bitmapPool = bitmapPool, - referenceCounter = referenceCounter, - strongMemoryCache = strongMemoryCache, - weakMemoryCache = weakMemoryCache, + memoryCache = memoryCache, callFactory = OkHttpClient(), eventListenerFactory = EventListener.Factory.NONE, componentRegistry = ComponentRegistry(), - addLastModifiedToFileCacheKey = true, - launchInterceptorChainOnMainThread = true, + options = ImageLoaderOptions(), logger = null ) val systemCallbacks = SystemCallbacks(imageLoader, context) diff --git a/coil-base/src/main/java/coil/ImageLoader.kt b/coil-base/src/main/java/coil/ImageLoader.kt index b63171a866..d45497f042 100644 --- a/coil-base/src/main/java/coil/ImageLoader.kt +++ b/coil-base/src/main/java/coil/ImageLoader.kt @@ -19,6 +19,7 @@ import coil.intercept.Interceptor import coil.map.Mapper import coil.memory.EmptyWeakMemoryCache import coil.memory.MemoryCache +import coil.memory.RealMemoryCache import coil.memory.RealWeakMemoryCache import coil.memory.StrongMemoryCache import coil.request.CachePolicy @@ -34,6 +35,7 @@ import coil.target.ViewTarget import coil.transition.CrossfadeTransition import coil.transition.Transition import coil.util.CoilUtils +import coil.util.ImageLoaderOptions import coil.util.Logger import coil.util.Utils import coil.util.getDrawableCompat @@ -52,9 +54,6 @@ import java.io.File * * Image loaders are designed to be shareable and work best when you create a single instance and * share it throughout your app. - * - * It's recommended, though not required, to call [shutdown] when you've finished using an image loader. - * This preemptively frees its memory and cleans up any observers. */ interface ImageLoader { @@ -95,29 +94,64 @@ interface ImageLoader { /** * Shutdown this image loader. * - * All associated resources will be freed and any new requests will fail before starting. + * All associated resources will be freed and new requests will fail before starting. + * + * Shutting down an image loader is optional. It will be cleaned up automatically if dereferenced. * * In progress [enqueue] requests will be cancelled immediately. * In progress [execute] requests will continue until complete. */ fun shutdown() - class Builder(context: Context) { - - private val applicationContext = context.applicationContext - - private var callFactory: Call.Factory? = null - private var eventListenerFactory: EventListener.Factory? = null - private var registry: ComponentRegistry? = null - private var logger: Logger? = null - private var defaults = DefaultRequestOptions.INSTANCE + /** + * Create an [ImageLoader.Builder] that shares the same resources and configuration as this image loader. + */ + fun newBuilder(): Builder + + class Builder { + + private val applicationContext: Context + private var defaults: DefaultRequestOptions + private var callFactory: Call.Factory? + private var eventListenerFactory: EventListener.Factory? + private var componentRegistry: ComponentRegistry? + private var options: ImageLoaderOptions + private var logger: Logger? + private var memoryCache: RealMemoryCache? + private var availableMemoryPercentage: Double + private var bitmapPoolPercentage: Double + private var bitmapPoolingEnabled: Boolean + private var trackWeakReferences: Boolean + + constructor(context: Context) { + applicationContext = context.applicationContext + defaults = DefaultRequestOptions.INSTANCE + callFactory = null + eventListenerFactory = null + componentRegistry = null + options = ImageLoaderOptions() + logger = null + memoryCache = null + availableMemoryPercentage = Utils.getDefaultAvailableMemoryPercentage(applicationContext) + bitmapPoolPercentage = Utils.getDefaultBitmapPoolPercentage() + bitmapPoolingEnabled = true + trackWeakReferences = true + } - private var availableMemoryPercentage = Utils.getDefaultAvailableMemoryPercentage(applicationContext) - private var bitmapPoolPercentage = Utils.getDefaultBitmapPoolPercentage() - private var addLastModifiedToFileCacheKey = true - private var bitmapPoolingEnabled = true - private var launchInterceptorChainOnMainThread = true - private var trackWeakReferences = true + internal constructor(imageLoader: RealImageLoader) { + applicationContext = imageLoader.context.applicationContext + defaults = imageLoader.defaults + callFactory = imageLoader.callFactory + eventListenerFactory = imageLoader.eventListenerFactory + componentRegistry = imageLoader.componentRegistry + options = imageLoader.options + logger = imageLoader.logger + memoryCache = imageLoader.memoryCache + availableMemoryPercentage = 0.0 + bitmapPoolPercentage = 0.0 + bitmapPoolingEnabled = true + trackWeakReferences = true + } /** * Set the [OkHttpClient] used for network requests. @@ -180,7 +214,19 @@ interface ImageLoader { * Set the [ComponentRegistry]. */ fun componentRegistry(registry: ComponentRegistry) = apply { - this.registry = registry + this.componentRegistry = registry + } + + /** + * Set the [MemoryCache]. This also sets the [BitmapPool] to the instance used by this [MemoryCache]. + * + * This is useful for sharing [MemoryCache] and [BitmapPool] instances between [ImageLoader]s. + * + * NOTE: Custom memory cache implementations are currently not supported. + */ + fun memoryCache(memoryCache: MemoryCache) = apply { + require(memoryCache is RealMemoryCache) { "Custom memory cache implementations are currently not supported." } + this.memoryCache = memoryCache } /** @@ -188,11 +234,14 @@ interface ImageLoader { * * Setting this to 0 disables memory caching and bitmap pooling. * + * Setting this value discards the shared memory cache set in [memoryCache]. + * * Default: [Utils.getDefaultAvailableMemoryPercentage] */ fun availableMemoryPercentage(@FloatRange(from = 0.0, to = 1.0) percent: Double) = apply { require(percent in 0.0..1.0) { "Percent must be in the range [0.0, 1.0]." } this.availableMemoryPercentage = percent + this.memoryCache = null } /** @@ -203,11 +252,14 @@ interface ImageLoader { * * Setting this to 0 disables bitmap pooling. * + * Setting this value discards the shared memory cache set in [memoryCache]. + * * Default: [Utils.getDefaultBitmapPoolPercentage] */ fun bitmapPoolPercentage(@FloatRange(from = 0.0, to = 1.0) percent: Double) = apply { require(percent in 0.0..1.0) { "Percent must be in the range [0.0, 1.0]." } this.bitmapPoolPercentage = percent + this.memoryCache = null } /** @@ -255,7 +307,7 @@ interface ImageLoader { * Default: true */ fun addLastModifiedToFileCacheKey(enable: Boolean) = apply { - this.addLastModifiedToFileCacheKey = enable + this.options = this.options.copy(addLastModifiedToFileCacheKey = enable) } /** @@ -267,10 +319,28 @@ interface ImageLoader { * If this is disabled, no bitmaps will be added to this [ImageLoader]'s [BitmapPool] automatically and * the [BitmapPool] will not be allocated any memory (this overrides [bitmapPoolPercentage]). * + * Setting this value discards the shared memory cache set in [memoryCache]. + * * Default: true */ fun bitmapPoolingEnabled(enable: Boolean) = apply { this.bitmapPoolingEnabled = enable + this.memoryCache = null + } + + /** + * Enables weak reference tracking of loaded images. + * + * This allows the image loader to hold weak references to loaded images. + * This ensures that if an image is still in memory it will be returned from the memory cache. + * + * Setting this value discards the shared memory cache set in [memoryCache]. + * + * Default: true + */ + fun trackWeakReferences(enable: Boolean) = apply { + this.trackWeakReferences = enable + this.memoryCache = null } /** @@ -295,19 +365,7 @@ interface ImageLoader { * Default: true */ fun launchInterceptorChainOnMainThread(enable: Boolean) = apply { - this.launchInterceptorChainOnMainThread = enable - } - - /** - * Enables weak reference tracking of loaded images. - * - * This allows the image loader to hold weak references to loaded images. - * This ensures that if an image is still in memory it will be returned from the memory cache. - * - * Default: true - */ - fun trackWeakReferences(enable: Boolean) = apply { - this.trackWeakReferences = enable + this.options = this.options.copy(launchInterceptorChainOnMainThread = enable) } /** @@ -447,6 +505,27 @@ interface ImageLoader { * Create a new [ImageLoader] instance. */ fun build(): ImageLoader { + val memoryCache = memoryCache ?: buildDefaultMemoryCache() + return RealImageLoader( + context = applicationContext, + defaults = defaults, + bitmapPool = memoryCache.bitmapPool, + memoryCache = memoryCache, + callFactory = callFactory ?: buildDefaultCallFactory(), + eventListenerFactory = eventListenerFactory ?: EventListener.Factory.NONE, + componentRegistry = componentRegistry ?: ComponentRegistry(), + options = options, + logger = logger + ) + } + + private fun buildDefaultCallFactory() = lazyCallFactory { + OkHttpClient.Builder() + .cache(CoilUtils.createDefaultCache(applicationContext)) + .build() + } + + private fun buildDefaultMemoryCache(): RealMemoryCache { val availableMemorySize = Utils.calculateAvailableMemorySize(applicationContext, availableMemoryPercentage) val bitmapPoolPercentage = if (bitmapPoolingEnabled) bitmapPoolPercentage else 0.0 val bitmapPoolSize = (bitmapPoolPercentage * availableMemorySize).toInt() @@ -468,27 +547,7 @@ interface ImageLoader { EmptyBitmapReferenceCounter } val strongMemoryCache = StrongMemoryCache(weakMemoryCache, referenceCounter, memoryCacheSize, logger) - - return RealImageLoader( - context = applicationContext, - defaults = defaults, - bitmapPool = bitmapPool, - referenceCounter = referenceCounter, - strongMemoryCache = strongMemoryCache, - weakMemoryCache = weakMemoryCache, - callFactory = callFactory ?: buildDefaultCallFactory(), - eventListenerFactory = eventListenerFactory ?: EventListener.Factory.NONE, - componentRegistry = registry ?: ComponentRegistry(), - addLastModifiedToFileCacheKey = addLastModifiedToFileCacheKey, - launchInterceptorChainOnMainThread = launchInterceptorChainOnMainThread, - logger = logger - ) - } - - private fun buildDefaultCallFactory() = lazyCallFactory { - OkHttpClient.Builder() - .cache(CoilUtils.createDefaultCache(applicationContext)) - .build() + return RealMemoryCache(strongMemoryCache, weakMemoryCache, referenceCounter, bitmapPool) } } diff --git a/coil-base/src/main/java/coil/RealImageLoader.kt b/coil-base/src/main/java/coil/RealImageLoader.kt index 529a94e721..a1748bff7f 100644 --- a/coil-base/src/main/java/coil/RealImageLoader.kt +++ b/coil-base/src/main/java/coil/RealImageLoader.kt @@ -5,7 +5,6 @@ import android.graphics.Bitmap import android.util.Log import androidx.annotation.MainThread import coil.bitmap.BitmapPool -import coil.bitmap.BitmapReferenceCounter import coil.decode.BitmapFactoryDecoder import coil.decode.DrawableDecoderService import coil.fetch.AssetUriFetcher @@ -26,9 +25,7 @@ import coil.memory.DelegateService import coil.memory.MemoryCacheService import coil.memory.RealMemoryCache import coil.memory.RequestService -import coil.memory.StrongMemoryCache import coil.memory.TargetDelegate -import coil.memory.WeakMemoryCache import coil.request.BaseTargetDisposable import coil.request.DefaultRequestOptions import coil.request.Disposable @@ -42,6 +39,7 @@ import coil.request.ViewTargetDisposable import coil.size.Size import coil.target.ViewTarget import coil.util.Emoji +import coil.util.ImageLoaderOptions import coil.util.Logger import coil.util.SystemCallbacks import coil.util.Utils.REQUEST_TYPE_ENQUEUE @@ -67,26 +65,23 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlin.coroutines.coroutineContext internal class RealImageLoader( - context: Context, + val context: Context, override val defaults: DefaultRequestOptions, override val bitmapPool: BitmapPool, - private val referenceCounter: BitmapReferenceCounter, - private val strongMemoryCache: StrongMemoryCache, - private val weakMemoryCache: WeakMemoryCache, - callFactory: Call.Factory, - private val eventListenerFactory: EventListener.Factory, - componentRegistry: ComponentRegistry, - addLastModifiedToFileCacheKey: Boolean, - private val launchInterceptorChainOnMainThread: Boolean, + override val memoryCache: RealMemoryCache, + val callFactory: Call.Factory, + val eventListenerFactory: EventListener.Factory, + val componentRegistry: ComponentRegistry, + val options: ImageLoaderOptions, val logger: Logger? ) : ImageLoader { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate + CoroutineExceptionHandler { _, throwable -> logger?.log(TAG, throwable) }) - private val delegateService = DelegateService(this, referenceCounter, logger) - private val memoryCacheService = MemoryCacheService(referenceCounter, strongMemoryCache, weakMemoryCache) + private val delegateService = DelegateService(this, memoryCache.referenceCounter, logger) + private val memoryCacheService = MemoryCacheService(memoryCache.referenceCounter, + memoryCache.strongMemoryCache, memoryCache.weakMemoryCache) private val requestService = RequestService(logger) - override val memoryCache = RealMemoryCache(strongMemoryCache, weakMemoryCache, referenceCounter) private val drawableDecoder = DrawableDecoderService(bitmapPool) private val systemCallbacks = SystemCallbacks(this, context) private val registry = componentRegistry.newBuilder() @@ -98,7 +93,7 @@ internal class RealImageLoader( // Fetchers .add(HttpUriFetcher(callFactory)) .add(HttpUrlFetcher(callFactory)) - .add(FileFetcher(addLastModifiedToFileCacheKey)) + .add(FileFetcher(options.addLastModifiedToFileCacheKey)) .add(AssetUriFetcher(context)) .add(ContentUriFetcher(context)) .add(ResourceUriFetcher(context, drawableDecoder)) @@ -107,8 +102,9 @@ internal class RealImageLoader( // Decoders .add(BitmapFactoryDecoder(context)) .build() - private val interceptors = registry.interceptors + EngineInterceptor(registry, bitmapPool, referenceCounter, - strongMemoryCache, memoryCacheService, requestService, systemCallbacks, drawableDecoder, logger) + private val interceptors = registry.interceptors + EngineInterceptor(registry, bitmapPool, + memoryCache.referenceCounter, memoryCache.strongMemoryCache, memoryCacheService, requestService, + systemCallbacks, drawableDecoder, logger) private val isShutdown = AtomicBoolean(false) override fun enqueue(request: ImageRequest): Disposable { @@ -171,7 +167,7 @@ internal class RealImageLoader( eventListener.onStart(request) request.listener?.onStart(request) } finally { - referenceCounter.decrement(cached) + memoryCache.referenceCounter.decrement(cached) } // Resolve the size. @@ -205,8 +201,8 @@ internal class RealImageLoader( /** Called by [SystemCallbacks.onTrimMemory]. */ fun onTrimMemory(level: Int) { - strongMemoryCache.trimMemory(level) - weakMemoryCache.trimMemory(level) + memoryCache.strongMemoryCache.trimMemory(level) + memoryCache.weakMemoryCache.trimMemory(level) bitmapPool.trimMemory(level) } @@ -216,11 +212,12 @@ internal class RealImageLoader( // Order is important. scope.cancel() systemCallbacks.shutdown() - strongMemoryCache.clearMemory() - weakMemoryCache.clearMemory() + memoryCache.clear() bitmapPool.clear() } + override fun newBuilder() = ImageLoader.Builder(this) + private suspend inline fun executeChain( request: ImageRequest, type: Int, @@ -229,7 +226,7 @@ internal class RealImageLoader( eventListener: EventListener ): ImageResult { val chain = RealInterceptorChain(request, type, interceptors, 0, request, size, cached, eventListener) - return if (launchInterceptorChainOnMainThread) { + return if (options.launchInterceptorChainOnMainThread) { chain.proceed(request) } else { withContext(request.dispatcher) { @@ -253,7 +250,7 @@ internal class RealImageLoader( eventListener.onSuccess(request, metadata) request.listener?.onSuccess(request, metadata) } finally { - referenceCounter.decrement(result.drawable) + memoryCache.referenceCounter.decrement(result.drawable) } } diff --git a/coil-base/src/main/java/coil/memory/RealMemoryCache.kt b/coil-base/src/main/java/coil/memory/RealMemoryCache.kt index 0c74ccd098..7a2974db38 100644 --- a/coil-base/src/main/java/coil/memory/RealMemoryCache.kt +++ b/coil-base/src/main/java/coil/memory/RealMemoryCache.kt @@ -1,13 +1,15 @@ package coil.memory import android.graphics.Bitmap +import coil.bitmap.BitmapPool import coil.bitmap.BitmapReferenceCounter import coil.memory.MemoryCache.Key internal class RealMemoryCache( - private val strongMemoryCache: StrongMemoryCache, - private val weakMemoryCache: WeakMemoryCache, - private val referenceCounter: BitmapReferenceCounter + val strongMemoryCache: StrongMemoryCache, + val weakMemoryCache: WeakMemoryCache, + val referenceCounter: BitmapReferenceCounter, + val bitmapPool: BitmapPool ) : MemoryCache { override val size get() = strongMemoryCache.size diff --git a/coil-base/src/main/java/coil/util/ImageLoaderOptions.kt b/coil-base/src/main/java/coil/util/ImageLoaderOptions.kt new file mode 100644 index 0000000000..7b1464f188 --- /dev/null +++ b/coil-base/src/main/java/coil/util/ImageLoaderOptions.kt @@ -0,0 +1,14 @@ +package coil.util + +import coil.ImageLoader +import coil.RealImageLoader + +/** + * Private configuration options used by [RealImageLoader]. + * + * @see ImageLoader.Builder + */ +internal data class ImageLoaderOptions( + val addLastModifiedToFileCacheKey: Boolean = true, + val launchInterceptorChainOnMainThread: Boolean = true +) diff --git a/coil-base/src/test/java/coil/bitmap/RealBitmapPoolTest.kt b/coil-base/src/test/java/coil/bitmap/RealBitmapPoolTest.kt index b01fe77a73..9c7072d048 100644 --- a/coil-base/src/test/java/coil/bitmap/RealBitmapPoolTest.kt +++ b/coil-base/src/test/java/coil/bitmap/RealBitmapPoolTest.kt @@ -36,7 +36,7 @@ class RealBitmapPoolTest { val bitmap = createBitmap() pool.put(bitmap) - assertEquals(bitmap, pool.get(100, 100, Bitmap.Config.ARGB_8888)) + assertEquals(bitmap, pool.getOrNull(100, 100, Bitmap.Config.ARGB_8888)) } @Test diff --git a/coil-base/src/test/java/coil/memory/RealMemoryCacheTest.kt b/coil-base/src/test/java/coil/memory/RealMemoryCacheTest.kt index e776d40c60..3900eaaabc 100644 --- a/coil-base/src/test/java/coil/memory/RealMemoryCacheTest.kt +++ b/coil-base/src/test/java/coil/memory/RealMemoryCacheTest.kt @@ -1,5 +1,6 @@ package coil.memory +import coil.bitmap.BitmapPool import coil.bitmap.FakeBitmapPool import coil.bitmap.RealBitmapReferenceCounter import coil.util.DEFAULT_BITMAP_SIZE @@ -18,6 +19,7 @@ import kotlin.test.assertTrue @RunWith(RobolectricTestRunner::class) class RealMemoryCacheTest { + private lateinit var bitmapPool: BitmapPool private lateinit var weakCache: WeakMemoryCache private lateinit var counter: RealBitmapReferenceCounter private lateinit var strongCache: StrongMemoryCache @@ -25,10 +27,11 @@ class RealMemoryCacheTest { @Before fun before() { + bitmapPool = FakeBitmapPool() weakCache = RealWeakMemoryCache(null) - counter = RealBitmapReferenceCounter(weakCache, FakeBitmapPool(), null) + counter = RealBitmapReferenceCounter(weakCache, bitmapPool, null) strongCache = StrongMemoryCache(weakCache, counter, Int.MAX_VALUE, null) - cache = RealMemoryCache(strongCache, weakCache, counter) + cache = RealMemoryCache(strongCache, weakCache, counter, bitmapPool) } @Test