Hello,
Java.Lang.RuntimeException: 'Canvas: trying to use a recycled bitmap android.graphics.Bitmap@50d15db'
This crash occurs when Android tries to draw a Bitmap that has already been disposed or recycled, while some view or drawable still holds a reference.
In a CarouselView
, item views are reused during swipes. If a Bitmap (or its backing stream) is shared, disposed too early, or reassigned late to a recycled view, the renderer can hit this error.
Recommended fixes
I recommend implementing the following practices. These are designed to ensure proper management of Bitmap lifecycles and prevent conflicts during view recycling:
- Avoid manual recycling while bound
Do not callBitmap.Recycle()
orBitmap.Dispose()
on images still attached to anImage
/ImageView
. Let the loader/GC manage it unless the view is detached (see detachment note below). - Do not share mutable Bitmap instances
Don’t assign the sameBitmap
object to multiple views/pages. If you must share, copy it:var bmpCopy = original.Copy(original.GetConfig() ?? Bitmap.Config.Argb8888, false);
- Prefer
ImageSource
over raw Bitmaps in MAUI
UseFileImageSource
,UriImageSource
, orStreamImageSource
instead of injecting AndroidBitmaps
into handlers. For streams, return a fresh stream each time:ImageSource.FromStream(() => File.OpenRead(path));
- Cancel in-flight loads on recycle
Keep aCancellationTokenSource
in your item view/control. OnOnBindingContextChanged
orUnloaded
, cancel pending loads before starting new ones to avoid late assignments on recycled views. - Detach before disposal (when you manage bitmaps)
On Android, callimageView.SetImageDrawable(null);
to detach the image, ensure no other view references it, then dispose the oldBitmap
. - Clear sources on unbind/unload
In templates, setImage.Source = null
duringUnloaded
or when theBindingContext
changes to release references as items leave the viewport. - Downsample large images
Decode to target dimensions to reduce memory pressure. When decoding manually, useBitmapFactory.Options
withInJustDecodeBounds
then setInSampleSize
. - Use fresh, open streams
WithStreamImageSource
, don’t close or reuse a singleMemoryStream
. The factory delegate must provide a new, open stream on each call. - Avoid assigning the same bitmap during swipes
Because CarouselView reuses views, ensure each binding gets its own data/stream to prevent concurrent reuse.
Implementation checklist
Since I don't have any information about your codebase, you could review the following checklist:
- Search for
Bitmap.Recycle()
,Bitmap.Dispose()
, orusing
around UI-bound images; move disposal to post-detachment points. - Verify no static/shared bitmap caches are recycled while visible.
- Ensure
StreamImageSource
factories always return new streams and don’t close them prematurely. - Add cancellation logic to image loads in item templates; clear
Image.Source
onUnloaded
/context changes. - Downsample large images before creating bitmaps/streams to match display needs.
References