Skip to content

Commit 05cd1a6

Browse files
Bugfix: Android crash in BasemapGallery due to Canvas trying to use a recycled bitmap Exception (#664)
* - Added a new `ThumbnailImage` class in `BasemapGallery` to address a known issue with recycled bitmaps in MAUI `CollectionView` on Android, preventing related Java exceptions. - Added a `using` statement for the stream from `Thumbnail.GetEncodedBufferAsync()`, ensuring proper disposal. * Update BasemapGallery.Appearance.cs
1 parent d7e1334 commit 05cd1a6

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

src/Toolkit/Toolkit.Maui/BasemapGallery/BasemapGallery.Appearance.cs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
// ******************************************************************************/
1616

1717
using Microsoft.Maui.Controls;
18-
using Microsoft.Maui.Controls.Shapes;
18+
using Microsoft.Maui.Controls.Shapes;
19+
using Microsoft.Maui.Platform;
1920

2021
namespace Esri.ArcGISRuntime.Toolkit.Maui;
2122

@@ -48,8 +49,25 @@ static BasemapGallery()
4849
outerScrimContainer.RowDefinitions.Add(new RowDefinition { Height = new GridLength(40) });
4950
outerScrimContainer.RowSpacing = 4;
5051

51-
Grid thumbnailGrid = new Grid() { WidthRequest = 64, HeightRequest = 64 };
52+
Grid thumbnailGrid = new Grid() { WidthRequest = 64, HeightRequest = 64 };
53+
#if ANDROID
54+
// Workaround for .NET MAUI Android bug where CollectionView throws
55+
// Java.Lang.RuntimeException: 'Canvas: trying to use a recycled bitmap'
56+
// (see https://github.com/dotnet/maui/issues/11519, affects MAUI 9.0.0).
57+
// This occurs when images are reused in CollectionView, causing recycled bitmaps to be drawn.
58+
// The workaround uses a custom ThumbnailImage control and a custom ImageHandler mapping
59+
// to clear the native image view when the image source changes, preventing the recycled bitmap error.
60+
ThumbnailImage thumbnail = new ThumbnailImage { Aspect = Aspect.AspectFill, BackgroundColor = Colors.Transparent, HorizontalOptions = LayoutOptions.Center };
61+
Microsoft.Maui.Handlers.ImageHandler.Mapper.PrependToMapping(nameof(Microsoft.Maui.IImage.Source), static (handler, view) =>
62+
{
63+
if (view is ThumbnailImage)
64+
{
65+
handler.PlatformView?.Clear();
66+
}
67+
});
68+
#else
5269
Image thumbnail = new Image { Aspect = Aspect.AspectFill, BackgroundColor = Colors.Transparent, HorizontalOptions = LayoutOptions.Center };
70+
#endif
5371
Border itemTypeBorder = new Border
5472
{
5573
StrokeShape = new RoundRectangle { CornerRadius = new CornerRadius(7) },
@@ -385,4 +403,16 @@ protected override void OnSizeAllocated(double width, double height)
385403
// Size change could necesitate a switch between list and grid presentations.
386404
HandleTemplateChange(width);
387405
}
388-
}
406+
}
407+
408+
#if ANDROID
409+
/// <summary>
410+
/// Represents an image used in the Basemap Gallery.
411+
/// <remarks>
412+
/// This class is only used on Android to work around a .NET MAUI bug where CollectionView may attempt to use a recycled bitmap,
413+
/// resulting in a Java.Lang.RuntimeException. By using a custom Image control and clearing the native image view when the source changes,
414+
/// this issue is avoided. See https://github.com/dotnet/maui/issues/11519 for more details.
415+
/// </remarks>
416+
/// </summary>
417+
internal class ThumbnailImage : Image { }
418+
#endif

src/Toolkit/Toolkit/UI/Controls/BasemapGallery/BasemapGalleryItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private async Task LoadImage()
111111

112112
if (Thumbnail?.LoadStatus == LoadStatus.Loaded)
113113
{
114-
var stream = await Thumbnail.GetEncodedBufferAsync();
114+
using var stream = await Thumbnail.GetEncodedBufferAsync();
115115
var buffer = new byte[stream.Length];
116116
#if NET8_0_OR_GREATER
117117
await stream.ReadExactlyAsync(buffer);

0 commit comments

Comments
 (0)