Skip to content

Commit

Permalink
Interrupt thread while decoding if coroutine is cancelled. Update Cor…
Browse files Browse the repository at this point in the history
…outines (1.3.6).
  • Loading branch information
colinrtwhite committed May 11, 2020
1 parent 541393d commit 4b61624
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 14 deletions.
32 changes: 24 additions & 8 deletions coil-base/src/main/java/coil/decode/BitmapFactoryDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import coil.size.PixelSize
import coil.size.Size
import coil.util.toDrawable
import coil.util.toSoftware
import kotlinx.coroutines.runInterruptible
import okio.Buffer
import okio.BufferedSource
import okio.ForwardingSource
Expand Down Expand Up @@ -47,6 +48,13 @@ internal class BitmapFactoryDecoder(private val context: Context) : Decoder {
source: BufferedSource,
size: Size,
options: Options
): DecodeResult = runInterruptible { decodeInterruptible(pool, source, size, options) }

private fun decodeInterruptible(
pool: BitmapPool,
source: BufferedSource,
size: Size,
options: Options
): DecodeResult = BitmapFactory.Options().run {
val safeSource = ExceptionCatchingSource(source)
val safeBufferedSource = safeSource.buffer()
Expand Down Expand Up @@ -154,21 +162,29 @@ internal class BitmapFactoryDecoder(private val context: Context) : Decoder {
}
}

// Keep a reference to the input bitmap so it can be returned to
// the pool if the decode doesn't complete successfully.
val inBitmap: Bitmap? = inBitmap

// Decode the bitmap.
val rawBitmap: Bitmap? = safeBufferedSource.use {
BitmapFactory.decodeStream(it.inputStream(), null, this)
}
safeSource.exception?.let { exception ->
rawBitmap?.let(pool::put)
throw exception
var outBitmap: Bitmap? = null
try {
outBitmap = safeBufferedSource.use {
BitmapFactory.decodeStream(it.inputStream(), null, this)
}
safeSource.exception?.let { throw it }
} catch (throwable: Throwable) {
inBitmap?.let(pool::put)
outBitmap?.let(pool::put)
throw throwable
}

// Apply any EXIF transformations.
checkNotNull(rawBitmap) {
checkNotNull(outBitmap) {
"BitmapFactory returned a null Bitmap. Often this means BitmapFactory could not decode the image data " +
"read from the input source (e.g. network or disk) as it's not encoded as a valid image format."
}
val bitmap = applyExifTransformations(pool, rawBitmap, inPreferredConfig, isFlipped, rotationDegrees)
val bitmap = applyExifTransformations(pool, outBitmap, inPreferredConfig, isFlipped, rotationDegrees)
bitmap.density = Bitmap.DENSITY_NONE

DecodeResult(
Expand Down
5 changes: 3 additions & 2 deletions coil-gif/src/main/java/coil/decode/GifDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import coil.bitmappool.BitmapPool
import coil.drawable.MovieDrawable
import coil.extension.repeatCount
import coil.size.Size
import kotlinx.coroutines.runInterruptible
import okio.BufferedSource

/**
Expand All @@ -31,7 +32,7 @@ class GifDecoder : Decoder {
source: BufferedSource,
size: Size,
options: Options
): DecodeResult {
): DecodeResult = runInterruptible {
// Movie requires an InputStream to resettable on API 18 and below.
// Read the data as a ByteArray to work around this.
val movie = if (SDK_INT <= 18) {
Expand All @@ -58,7 +59,7 @@ class GifDecoder : Decoder {

drawable.setRepeatCount(options.parameters.repeatCount() ?: MovieDrawable.REPEAT_INFINITE)

return DecodeResult(
DecodeResult(
drawable = drawable,
isSampled = false
)
Expand Down
5 changes: 3 additions & 2 deletions coil-gif/src/main/java/coil/decode/ImageDecoderDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import coil.drawable.ScaleDrawable
import coil.extension.repeatCount
import coil.size.PixelSize
import coil.size.Size
import kotlinx.coroutines.runInterruptible
import okio.BufferedSource
import okio.sink
import java.io.File
Expand Down Expand Up @@ -44,7 +45,7 @@ class ImageDecoderDecoder : Decoder {
source: BufferedSource,
size: Size,
options: Options
): DecodeResult {
): DecodeResult = runInterruptible {
var tempFile: File? = null

try {
Expand Down Expand Up @@ -110,7 +111,7 @@ class ImageDecoderDecoder : Decoder {
baseDrawable
}

return DecodeResult(
DecodeResult(
drawable = drawable,
isSampled = isSampled
)
Expand Down
5 changes: 3 additions & 2 deletions coil-svg/src/main/java/coil/decode/SvgDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import coil.size.OriginalSize
import coil.size.PixelSize
import coil.size.Size
import com.caverock.androidsvg.SVG
import kotlinx.coroutines.runInterruptible
import okio.BufferedSource

/**
Expand All @@ -31,7 +32,7 @@ class SvgDecoder(private val context: Context) : Decoder {
source: BufferedSource,
size: Size,
options: Options
): DecodeResult {
): DecodeResult = runInterruptible {
val svg = source.use { SVG.getFromInputStream(it.inputStream()) }

val svgWidth = svg.documentWidth
Expand Down Expand Up @@ -82,7 +83,7 @@ class SvgDecoder(private val context: Context) : Decoder {
val bitmap = pool.get(bitmapWidth, bitmapHeight, config)
svg.renderToCanvas(Canvas(bitmap))

return DecodeResult(
DecodeResult(
drawable = bitmap.toDrawable(context.resources),
isSampled = true // SVGs can always be re-decoded at a higher resolution.
)
Expand Down
2 changes: 2 additions & 0 deletions coil-video/src/main/java/coil/fetch/VideoFrameFetcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ abstract class VideoFrameFetcher<T : Any>(private val context: Context) : Fetche
size: Size,
options: Options
): FetchResult {
// NOTE: we don't use 'runInterruptible' as MediaMetadataRetriever will
// continue its work even if its thread is interrupted.
val retriever = MediaMetadataRetriever()

try {
Expand Down

0 comments on commit 4b61624

Please sign in to comment.