Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace CoilLogger with Logger interface. #316

Merged
merged 3 commits into from
Mar 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions coil-base/src/main/java/coil/ImageLoaderBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import coil.size.Precision
import coil.transition.CrossfadeTransition
import coil.transition.Transition
import coil.util.CoilUtils
import coil.util.Logger
import coil.util.Utils
import coil.util.getDrawableCompat
import coil.util.lazyCallFactory
Expand All @@ -38,6 +39,7 @@ class ImageLoaderBuilder(context: Context) {

private var callFactory: Call.Factory? = null
private var registry: ComponentRegistry? = null
private var logger: Logger? = null
private var defaults = DefaultRequestOptions()

private var availableMemoryPercentage = Utils.getDefaultAvailableMemoryPercentage(applicationContext)
Expand Down Expand Up @@ -282,6 +284,15 @@ class ImageLoaderBuilder(context: Context) {
this.defaults = this.defaults.copy(networkCachePolicy = policy)
}

/**
* Set the [Logger] to write logs to.
*
* NOTE: Setting a non-null [Logger] can reduce performance and should be avoided in release builds.
*/
fun logger(logger: Logger?) = apply {
this.logger = logger
}

/**
* Create a new [ImageLoader] instance.
*/
Expand All @@ -290,10 +301,10 @@ class ImageLoaderBuilder(context: Context) {
val bitmapPoolSize = (bitmapPoolPercentage * availableMemorySize).toLong()
val memoryCacheSize = (availableMemorySize - bitmapPoolSize).toInt()

val bitmapPool = BitmapPool(bitmapPoolSize)
val bitmapPool = BitmapPool(bitmapPoolSize, logger)
val weakMemoryCache = if (trackWeakReferences) RealWeakMemoryCache() else EmptyWeakMemoryCache
val referenceCounter = BitmapReferenceCounter(weakMemoryCache, bitmapPool)
val memoryCache = MemoryCache(weakMemoryCache, referenceCounter, memoryCacheSize)
val referenceCounter = BitmapReferenceCounter(weakMemoryCache, bitmapPool, logger)
val memoryCache = MemoryCache(weakMemoryCache, referenceCounter, memoryCacheSize, logger)

return RealImageLoader(
context = applicationContext,
Expand All @@ -303,7 +314,8 @@ class ImageLoaderBuilder(context: Context) {
memoryCache = memoryCache,
weakMemoryCache = weakMemoryCache,
callFactory = callFactory ?: buildDefaultCallFactory(),
registry = registry ?: ComponentRegistry()
registry = registry ?: ComponentRegistry(),
logger = logger
)
}

Expand Down
22 changes: 12 additions & 10 deletions coil-base/src/main/java/coil/RealImageLoader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import coil.target.ViewTarget
import coil.transform.Transformation
import coil.util.ComponentCallbacks
import coil.util.Emoji
import coil.util.Logger
import coil.util.closeQuietly
import coil.util.emoji
import coil.util.firstNotNullIndices
Expand Down Expand Up @@ -95,20 +96,21 @@ internal class RealImageLoader(
private val memoryCache: MemoryCache,
private val weakMemoryCache: WeakMemoryCache,
callFactory: Call.Factory,
registry: ComponentRegistry
registry: ComponentRegistry,
private val logger: Logger?
) : ImageLoader, ComponentCallbacks {

companion object {
private const val TAG = "RealImageLoader"
}

private val loaderScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> log(TAG, throwable) }
private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> logger?.log(TAG, throwable) }

private val delegateService = DelegateService(this, referenceCounter)
private val requestService = RequestService()
private val delegateService = DelegateService(this, referenceCounter, logger)
private val requestService = RequestService(logger)
private val drawableDecoder = DrawableDecoderService(bitmapPool)
private val networkObserver = NetworkObserver(context)
private val networkObserver = NetworkObserver(context, logger)

private val registry = registry.newBuilder()
// Mappers
Expand Down Expand Up @@ -212,7 +214,7 @@ internal class RealImageLoader(

// Short circuit if the cached drawable is valid for the target.
if (cachedDrawable != null && isCachedDrawableValid(cachedDrawable, cachedValue.isSampled, size, scale, request)) {
log(TAG, Log.INFO) { "${Emoji.BRAIN} Cached - $data" }
logger?.log(TAG, Log.INFO) { "${Emoji.BRAIN} Cached - $data" }
targetDelegate.success(cachedDrawable, true, request.transition)
request.listener?.onSuccess(data, DataSource.MEMORY)
return@innerJob cachedDrawable
Expand All @@ -227,7 +229,7 @@ internal class RealImageLoader(
}

// Set the final result on the target.
log(TAG, Log.INFO) { "${source.emoji} Successful (${source.name}) - $data" }
logger?.log(TAG, Log.INFO) { "${source.emoji} Successful (${source.name}) - $data" }
targetDelegate.success(drawable, false, request.transition)
request.listener?.onSuccess(data, source)

Expand All @@ -244,10 +246,10 @@ internal class RealImageLoader(
throwable ?: return@launch

if (throwable is CancellationException) {
log(TAG, Log.INFO) { "${Emoji.CONSTRUCTION} Cancelled - $data" }
logger?.log(TAG, Log.INFO) { "${Emoji.CONSTRUCTION} Cancelled - $data" }
request.listener?.onCancel(data)
} else {
log(TAG, Log.INFO) { "${Emoji.SIREN} Failed - $data - $throwable" }
logger?.log(TAG, Log.INFO) { "${Emoji.SIREN} Failed - $data - $throwable" }
val drawable = if (throwable is NullRequestDataException) request.fallback else request.error
targetDelegate.error(drawable, request.transition)
request.listener?.onError(data, throwable)
Expand Down Expand Up @@ -428,7 +430,7 @@ internal class RealImageLoader(
val baseBitmap = if (result.drawable is BitmapDrawable) {
result.drawable.bitmap
} else {
log(TAG, Log.INFO) {
logger?.log(TAG, Log.INFO) {
"Converting drawable of type ${result.drawable::class.java.canonicalName} " +
"to apply transformations: $transformations"
}
Expand Down
6 changes: 5 additions & 1 deletion coil-base/src/main/java/coil/bitmappool/BitmapPool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.ComponentCallbacks2
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.annotation.Px
import coil.util.Logger

/**
* An object pool that enables callers to reuse [Bitmap] objects.
Expand All @@ -18,7 +19,10 @@ interface BitmapPool {
*/
@JvmStatic
@JvmName("create")
operator fun invoke(maxSize: Long): BitmapPool = RealBitmapPool(maxSize)
operator fun invoke(
maxSize: Long,
logger: Logger? = null
): BitmapPool = RealBitmapPool(maxSize = maxSize, logger = logger)
}

/**
Expand Down
22 changes: 12 additions & 10 deletions coil-base/src/main/java/coil/bitmappool/RealBitmapPool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import android.util.Log
import androidx.annotation.Px
import androidx.collection.arraySetOf
import coil.bitmappool.strategy.BitmapPoolStrategy
import coil.util.Logger
import coil.util.getAllocationByteCountCompat
import coil.util.log

Expand All @@ -26,7 +27,8 @@ import coil.util.log
internal class RealBitmapPool(
private val maxSize: Long,
private val allowedConfigs: Set<Bitmap.Config> = getDefaultAllowedConfigs(),
private val strategy: BitmapPoolStrategy = BitmapPoolStrategy()
private val strategy: BitmapPoolStrategy = BitmapPoolStrategy(),
private val logger: Logger? = null
) : BitmapPool {

companion object {
Expand Down Expand Up @@ -64,7 +66,7 @@ internal class RealBitmapPool(
val size = bitmap.getAllocationByteCountCompat()

if (!bitmap.isMutable || size > maxSize || bitmap.config !in allowedConfigs) {
log(TAG, Log.VERBOSE) {
logger?.log(TAG, Log.VERBOSE) {
"Rejected bitmap from pool: bitmap: ${strategy.logBitmap(bitmap)}, " +
"is mutable: ${bitmap.isMutable}, " +
"is greater than max size: ${size > maxSize}" +
Expand All @@ -79,7 +81,7 @@ internal class RealBitmapPool(
puts++
currentSize += size

log(TAG, Log.VERBOSE) { "Put bitmap in pool=${strategy.logBitmap(bitmap)}" }
logger?.log(TAG, Log.VERBOSE) { "Put bitmap in pool=${strategy.logBitmap(bitmap)}" }
dump()

trimToSize(maxSize)
Expand Down Expand Up @@ -107,15 +109,15 @@ internal class RealBitmapPool(

val result = strategy.get(width, height, config)
if (result == null) {
log(TAG, Log.DEBUG) { "Missing bitmap=${strategy.logBitmap(width, height, config)}" }
logger?.log(TAG, Log.DEBUG) { "Missing bitmap=${strategy.logBitmap(width, height, config)}" }
misses++
} else {
hits++
currentSize -= result.getAllocationByteCountCompat()
normalize(result)
}

log(TAG, Log.VERBOSE) { "Get bitmap=${strategy.logBitmap(width, height, config)}" }
logger?.log(TAG, Log.VERBOSE) { "Get bitmap=${strategy.logBitmap(width, height, config)}" }
dump()

return result
Expand All @@ -124,13 +126,13 @@ internal class RealBitmapPool(
override fun clear() = clearMemory()

fun clearMemory() {
log(TAG, Log.DEBUG) { "clearMemory" }
logger?.log(TAG, Log.DEBUG) { "clearMemory" }
trimToSize(-1)
}

@Synchronized
override fun trimMemory(level: Int) {
log(TAG, Log.DEBUG) { "trimMemory, level=$level" }
logger?.log(TAG, Log.DEBUG) { "trimMemory, level=$level" }
if (level >= TRIM_MEMORY_BACKGROUND) {
clearMemory()
} else if (level in TRIM_MEMORY_RUNNING_LOW until TRIM_MEMORY_UI_HIDDEN) {
Expand All @@ -155,14 +157,14 @@ internal class RealBitmapPool(
while (currentSize > size) {
val removed = strategy.removeLast()
if (removed == null) {
log(TAG, Log.WARN) { "Size mismatch, resetting.\n${computeUnchecked()}" }
logger?.log(TAG, Log.WARN) { "Size mismatch, resetting.\n${computeUnchecked()}" }
currentSize = 0
return
}
currentSize -= removed.getAllocationByteCountCompat()
evictions++

log(TAG, Log.DEBUG) { "Evicting bitmap=${strategy.logBitmap(removed)}" }
logger?.log(TAG, Log.DEBUG) { "Evicting bitmap=${strategy.logBitmap(removed)}" }
dump()

removed.recycle()
Expand All @@ -174,7 +176,7 @@ internal class RealBitmapPool(
}

private fun dump() {
log(TAG, Log.VERBOSE) { computeUnchecked() }
logger?.log(TAG, Log.VERBOSE) { computeUnchecked() }
}

private fun computeUnchecked(): String {
Expand Down
8 changes: 5 additions & 3 deletions coil-base/src/main/java/coil/memory/BitmapReferenceCounter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.core.util.set
import coil.bitmappool.BitmapPool
import coil.collection.SparseIntArraySet
import coil.extension.plusAssign
import coil.util.Logger
import coil.util.identityHashCode
import coil.util.log
import java.lang.ref.WeakReference
Expand All @@ -24,7 +25,8 @@ import java.lang.ref.WeakReference
*/
internal class BitmapReferenceCounter(
private val weakMemoryCache: WeakMemoryCache,
private val bitmapPool: BitmapPool
private val bitmapPool: BitmapPool,
private val logger: Logger?
) {

companion object {
Expand All @@ -42,7 +44,7 @@ internal class BitmapReferenceCounter(
val count = counts[key]
val newCount = count + 1
counts[key] = newCount
log(TAG, Log.VERBOSE) { "INCREMENT: [$key, $newCount]" }
logger?.log(TAG, Log.VERBOSE) { "INCREMENT: [$key, $newCount]" }
}

/**
Expand All @@ -57,7 +59,7 @@ internal class BitmapReferenceCounter(
val count = counts[key]
val newCount = count - 1
counts[key] = newCount
log(TAG, Log.VERBOSE) { "DECREMENT: [$key, $newCount]" }
logger?.log(TAG, Log.VERBOSE) { "DECREMENT: [$key, $newCount]" }

if (newCount <= 0) {
counts.delete(key)
Expand Down
8 changes: 5 additions & 3 deletions coil-base/src/main/java/coil/memory/DelegateService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import coil.request.Request
import coil.target.PoolableViewTarget
import coil.target.Target
import coil.target.ViewTarget
import coil.util.Logger
import coil.util.requestManager
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Deferred
Expand All @@ -20,7 +21,8 @@ import kotlinx.coroutines.Deferred
*/
internal class DelegateService(
private val imageLoader: ImageLoader,
private val referenceCounter: BitmapReferenceCounter
private val referenceCounter: BitmapReferenceCounter,
private val logger: Logger?
) {

/** Wrap the [request]'s [Target] to support [Bitmap] pooling. */
Expand All @@ -29,8 +31,8 @@ internal class DelegateService(
is GetRequest -> InvalidatableEmptyTargetDelegate(referenceCounter)
is LoadRequest -> when (val target = request.target) {
null -> EmptyTargetDelegate
is PoolableViewTarget<*> -> PoolableTargetDelegate(target, referenceCounter)
else -> InvalidatableTargetDelegate(target, referenceCounter)
is PoolableViewTarget<*> -> PoolableTargetDelegate(target, referenceCounter, logger)
else -> InvalidatableTargetDelegate(target, referenceCounter, logger)
}
}
}
Expand Down
13 changes: 7 additions & 6 deletions coil-base/src/main/java/coil/memory/HardwareBitmapService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.annotation.WorkerThread
import coil.memory.HardwareBitmapBlacklist.IS_BLACKLISTED
import coil.size.PixelSize
import coil.size.Size
import coil.util.Logger
import coil.util.log
import java.io.File

Expand All @@ -25,13 +26,13 @@ internal sealed class HardwareBitmapService {
}

/** Return true if we can currently use [Bitmap.Config.HARDWARE]. */
abstract fun allowHardware(size: Size): Boolean
abstract fun allowHardware(size: Size, logger: Logger?): Boolean
}

/** Returns a fixed value for [allowHardware]. */
private class ImmutableHardwareBitmapService(private val allowHardware: Boolean) : HardwareBitmapService() {

override fun allowHardware(size: Size) = allowHardware
override fun allowHardware(size: Size, logger: Logger?) = allowHardware
}

/**
Expand Down Expand Up @@ -60,18 +61,18 @@ private object LimitedFileDescriptorHardwareBitmapService : HardwareBitmapServic
@Volatile private var decodesSinceLastFileDescriptorCheck = 0
@Volatile private var hasAvailableFileDescriptors = true

override fun allowHardware(size: Size): Boolean {
override fun allowHardware(size: Size, logger: Logger?): Boolean {
// Don't use up file descriptors on small bitmaps.
if (size is PixelSize && (size.width < MIN_SIZE_DIMENSION || size.height < MIN_SIZE_DIMENSION)) {
return false
}

return hasAvailableFileDescriptors()
return hasAvailableFileDescriptors(logger)
}

@Synchronized
@WorkerThread
private fun hasAvailableFileDescriptors(): Boolean {
private fun hasAvailableFileDescriptors(logger: Logger?): Boolean {
// Only check if we have available file descriptors after a
// set amount of decodes since it's expensive (1-2 milliseconds).
if (decodesSinceLastFileDescriptorCheck++ >= FILE_DESCRIPTOR_CHECK_INTERVAL) {
Expand All @@ -81,7 +82,7 @@ private object LimitedFileDescriptorHardwareBitmapService : HardwareBitmapServic
hasAvailableFileDescriptors = numUsedFileDescriptors < FILE_DESCRIPTOR_LIMIT

if (hasAvailableFileDescriptors) {
log(TAG, Log.WARN) {
logger?.log(TAG, Log.WARN) {
"Unable to allocate more hardware bitmaps. Number of used file descriptors: $numUsedFileDescriptors"
}
}
Expand Down
Loading