fix: RequestBuilder.submitAsync() throwing an Exception if the Glide request is restarted (#4569)

This is the third attempt to fix `RequestBuilder.submitAsync()`. For the
rationale, see the comments of #4436.

We now clear the continuation reference after resuming it, to make sure
that:
1) It will only be resumed once
2) It will not leak the coroutine when Glide keeps the `Request` around.
This commit is contained in:
Christophe Beyls 2024-07-14 09:01:01 +02:00 committed by GitHub
parent 7c09c6aa2b
commit 12040b90fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 36 additions and 26 deletions

View File

@ -5,6 +5,7 @@ import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
@ -17,15 +18,25 @@ suspend fun <R> RequestBuilder<R>.submitAsync(
height: Int = Target.SIZE_ORIGINAL height: Int = Target.SIZE_ORIGINAL
): R { ): R {
return suspendCancellableCoroutine { continuation -> return suspendCancellableCoroutine { continuation ->
val target = addListener( val target = addListener(ContinuationRequestListener(continuation))
object : RequestListener<R> { .submit(width, height)
continuation.invokeOnCancellation { target.cancel(true) }
}
}
private class ContinuationRequestListener<R>(continuation: Continuation<R>) : RequestListener<R> {
private var continuation: Continuation<R>? = continuation
override fun onLoadFailed( override fun onLoadFailed(
e: GlideException?, e: GlideException?,
model: Any?, model: Any?,
target: Target<R>, target: Target<R>,
isFirstResource: Boolean isFirstResource: Boolean
): Boolean { ): Boolean {
continuation.resumeWithException(e ?: GlideException("Image loading failed")) continuation?.let {
continuation = null
it.resumeWithException(e ?: GlideException("Image loading failed"))
}
return false return false
} }
@ -36,13 +47,12 @@ suspend fun <R> RequestBuilder<R>.submitAsync(
dataSource: DataSource, dataSource: DataSource,
isFirstResource: Boolean isFirstResource: Boolean
): Boolean { ): Boolean {
continuation?.let {
if (target?.request?.isComplete == true) { if (target?.request?.isComplete == true) {
continuation.resume(resource) continuation = null
it.resume(resource)
}
} }
return false return false
} }
} }
).submit(width, height)
continuation.invokeOnCancellation { target.cancel(true) }
}
}