From 12040b90fbe5a2f540b3016333e2408245a822e4 Mon Sep 17 00:00:00 2001 From: Christophe Beyls Date: Sun, 14 Jul 2024 09:01:01 +0200 Subject: [PATCH] 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. --- .../tusky/util/GlideExtensions.kt | 62 +++++++++++-------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/GlideExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/GlideExtensions.kt index 01629c731..a328a8e7c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/GlideExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/GlideExtensions.kt @@ -5,6 +5,7 @@ import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target +import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlinx.coroutines.suspendCancellableCoroutine @@ -17,32 +18,41 @@ suspend fun RequestBuilder.submitAsync( height: Int = Target.SIZE_ORIGINAL ): R { return suspendCancellableCoroutine { continuation -> - val target = addListener( - object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target, - isFirstResource: Boolean - ): Boolean { - continuation.resumeWithException(e ?: GlideException("Image loading failed")) - return false - } - - override fun onResourceReady( - resource: R & Any, - model: Any, - target: Target?, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - if (target?.request?.isComplete == true) { - continuation.resume(resource) - } - return false - } - } - ).submit(width, height) + val target = addListener(ContinuationRequestListener(continuation)) + .submit(width, height) continuation.invokeOnCancellation { target.cancel(true) } } } + +private class ContinuationRequestListener(continuation: Continuation) : RequestListener { + private var continuation: Continuation? = continuation + + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { + continuation?.let { + continuation = null + it.resumeWithException(e ?: GlideException("Image loading failed")) + } + return false + } + + override fun onResourceReady( + resource: R & Any, + model: Any, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + continuation?.let { + if (target?.request?.isComplete == true) { + continuation = null + it.resume(resource) + } + } + return false + } +}