Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[Android] Fix CollectionView item renderer disposal on Android (Memory Leak) #14882

Merged
merged 3 commits into from
Nov 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<controls:TestContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:Xamarin.Forms.Controls.Issues"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue14880"
Title="Issue 14880">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackLayout Orientation="Vertical" Spacing="5" Grid.Row="0" VerticalOptions="Center">
<Label LineBreakMode="WordWrap" Text="Place a breakpoint in the Dispose method of CheckBoxRendererBase.cs for the Android platform, then click the Back button. Does the breakpoint halt the program? If so, good. The Dispose method was called and the test passed." HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Padding="30,10,30,10"/>
</StackLayout>
<CollectionView Grid.Row="1" ItemsSource="{Binding Items}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal" Spacing="10" BackgroundColor="Beige" Padding="10">
<CheckBox/>
<Label Text="{Binding Text}" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</controls:TestContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Collections.ObjectModel;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 14880, "[Bug] CollectionView item renderers never get disposed on Android (Memory Leak)", PlatformAffected.Android)]
public partial class Issue14880 : TestContentPage
{
public Issue14880()
{
#if APP
InitializeComponent();
BindingContext = new Issue14880ViewModel();
#endif
}

protected override void Init()
{
}
}

[Preserve(AllMembers = true)]
public class Issue14880ViewModel
{
public ObservableCollection<Issue14880Model> Items { get; set; }

public Issue14880ViewModel()
{
var collection = new ObservableCollection<Issue14880Model>();
var pageSize = 5;

for (var i = 0; i < pageSize; i++)
{
collection.Add(new Issue14880Model()
{
Text = "Item " + i,
});
}

Items = collection;
}
}

[Preserve(AllMembers = true)]
public class Issue14880Model
{
public string Text { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<DependentUpon>Issue14765.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue14764.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue14880.xaml.cs">
<DependentUpon>Issue14880.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue13573.xaml.cs">
<DependentUpon>Issue13573.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -2336,7 +2340,7 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8383-2.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8384.xaml">
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8384.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue13577.xaml">
Expand Down Expand Up @@ -2951,4 +2955,10 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue14880.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ internal void Recycle()
RemoveView(Content.View);
}

Content?.Dispose();

Content = null;
_size = null;
}
Expand Down
11 changes: 11 additions & 0 deletions Xamarin.Forms.Platform.Android/CollectionView/ItemsViewAdapter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Android.Content;
using Android.Widget;
using AndroidX.RecyclerView.Widget;
Expand All @@ -12,6 +13,7 @@ public class ItemsViewAdapter<TItemsView, TItemsViewSource> : RecyclerView.Adapt
where TItemsViewSource : IItemsViewSource
{
protected readonly TItemsView ItemsView;
protected readonly List<ItemContentView> ContentViews;
readonly Func<View, Context, ItemContentView> _createItemContentView;
protected internal TItemsViewSource ItemsSource;

Expand All @@ -21,6 +23,7 @@ public class ItemsViewAdapter<TItemsView, TItemsViewSource> : RecyclerView.Adapt
protected internal ItemsViewAdapter(TItemsView itemsView, Func<View, Context, ItemContentView> createItemContentView = null)
{
ItemsView = itemsView ?? throw new ArgumentNullException(nameof(itemsView));
ContentViews = new List<ItemContentView>();

UpdateUsingItemTemplate();

Expand Down Expand Up @@ -87,6 +90,8 @@ public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int

var itemContentView = _createItemContentView.Invoke(ItemsView, context);

ContentViews.Add(itemContentView);

return new TemplatedItemViewHolder(itemContentView, ItemsView.ItemTemplate);
}

Expand All @@ -109,6 +114,12 @@ protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var contentView in ContentViews)
{
contentView.Recycle();
}
ContentViews.Clear();

ItemsSource?.Dispose();
ItemsView.PropertyChanged -= ItemsViewPropertyChanged;
}
Expand Down