bumptech / glide

An image loading and caching library for Android focused on smooth scrolling
https://bumptech.github.io/glide/
Other
34.69k stars 6.13k forks source link

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@ea5bf3d #4983

Open animeshPE91 opened 1 year ago

animeshPE91 commented 1 year ago

Hi Glide team,

Recently after app release we started seeing this issue for few users.

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@ea5bf3d

The glide that we are using is com.github.bumptech.glide:glide:4.11.0

the retrofit library that we are using is com.squareup.retrofit2:retrofit:2.9.0

Following is the code snippet I am using for loading a image using glide into imageView

context?.let { context ->
            Glide.with(context)
                .load(story.url)
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .skipMemoryCache(true)
                .placeholder(binding.ivStory.drawable)
                .listener(object: RequestListener<Drawable> {
                    override fun onLoadFailed(
                        e: GlideException?,
                        model: Any?,
                        target: Target<Drawable>?,
                        isFirstResource: Boolean
                    ): Boolean {
                        return false
                    }

                    override fun onResourceReady(
                        resource: Drawable?,
                        model: Any?,
                        target: Target<Drawable>?,
                        dataSource: DataSource?,
                        isFirstResource: Boolean
                    ): Boolean {
                        binding.loader.visibility = View.GONE
                        animation.start()
                        return false
                    }
                }
                )
                .into(binding.ivStory)

The error that I am getting on firebase is as follows

Fatal Exception: java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@d7a7131
       at android.graphics.BaseCanvas.throwIfCannotDraw(BaseCanvas.java:55)
       at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:234)
       at android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:97)
       at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:529)
       at android.widget.ImageView.onDraw(ImageView.java:1376)
       at android.view.View.draw(View.java:19442)
       at android.view.View.updateDisplayListIfDirty(View.java:18392)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1975)
       at android.view.View.draw(View.java:19445)
       at android.view.View.updateDisplayListIfDirty(View.java:18392)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1975)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1975)
       at android.view.View.draw(View.java:19445)
       at android.view.View.updateDisplayListIfDirty(View.java:18392)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at androidx.drawerlayout.widget.DrawerLayout.drawChild(DrawerLayout.java:1426)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.draw(View.java:19445)
       at android.view.View.updateDisplayListIfDirty(View.java:18392)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.updateDisplayListIfDirty(View.java:18383)
       at android.view.View.draw(View.java:19170)
       at android.view.ViewGroup.drawChild(ViewGroup.java:4324)
       at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4087)
       at android.view.View.draw(View.java:19445)
       at com.android.internal.policy.DecorView.draw(DecorView.java:892)
       at android.view.View.updateDisplayListIfDirty(View.java:18392)
       at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:676)
       at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:682)
       at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:790)
       at android.view.ViewRootImpl.draw(ViewRootImpl.java:3503)
       at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3285)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2782)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1718)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7513)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1066)
       at android.view.Choreographer.doCallbacks(Choreographer.java:878)
       at android.view.Choreographer.doFrame(Choreographer.java:794)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1052)
       at android.os.Handler.handleCallback(Handler.java:790)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:210)
       at android.app.ActivityThread.main(ActivityThread.java:7080)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:523)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:863)

Can you please help me understand what I am doing wrong here; Or if i can replace/improve this code to avoid this issue

DearZack commented 1 year ago

Have you solved this problem

Madderate commented 10 months ago

I have a similar problem too, which is occurred after user opening app from background.

It's expected to set ImageView's imageDrawable null, if the Bitmap in Drawable the ImageView holds is recycled. But obviously, here the Drawable that ImageView holds is not set null.

I don't know whether system or Glide recycled that Bitmap.

The following is the code:

fun ImageView.setTransformedImageBitmap(
    imageUrl: String?,
    transformation: Transformation<Bitmap>,
    currentUrlFetcher: () -> String?
) {
    Glide.with(this)
        .asBitmap()
        .load(imageUrl)
        .transform(transformation)
        .transform(WebpDrawable::class.java, WebpDrawableTransformation(transformation))
        .transition(BitmapTransitionOptions.withCrossFade())
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .listener(
            object : RequestListener<Bitmap> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<Bitmap>,
                    isFirstResource: Boolean
                ): Boolean {
                    e?.run {
                        // some error logic
                    }
                    return false
                }

                override fun onResourceReady(
                    resource: Bitmap,
                    model: Any,
                    target: Target<Bitmap>,
                    dataSource: DataSource,
                    isFirstResource: Boolean
                ): Boolean {
                    val currentUrl = currentUrlFetcher()
                    val resourceValid = !resource.isRecycled
                    if (currentUrl != imageUrl) {
                        Log.e(TAG, "Image url are inconsistent!")
                        return true
                    }
                    if (!resourceValid) {
                        Log.e(TAG, "Resource invalid!")
                        return true
                    }
                    return false
                }
            }
        )
        .into(this)
}
davidlam-gc commented 7 months ago

@Madderate were you able to find a fix for this? having a similar issue when a user opens the app from the background as well

january-lab commented 4 weeks ago

is there any update, guys? Check is resource recycled which is not a quite good solution, the callback should do onLoadFailed() instead i think.

january-lab commented 2 days ago

Recently, the crash of recycled bitmap have been increased which is reported from Firebase Crashlytics. does anyone meet same problem? Workaround check is bitmap recycled before using which is not actually quite good solution, we need to handle same as onLoadFailed