From 3f88aebe22aaa0f8765648f15f1649c1a5ba2da4 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Tue, 2 Aug 2016 14:57:08 -0600 Subject: [PATCH] Dispose of child renderers in FrameRenderer (#265) * Dispose of child renderers in FrameRenderer * Add missing null check --- .../Bugzilla42329.cs | 149 ++++++++++++++++++ ...rin.Forms.Controls.Issues.Shared.projitems | 1 + .../AppCompat/FrameRenderer.cs | 21 ++- 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42329.cs diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42329.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42329.cs new file mode 100644 index 000000000..16cd3081d --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42329.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Xamarin.Forms.Controls.Issues; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +#if UITEST +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls +{ + [Preserve (AllMembers = true)] + [Issue (IssueTracker.Bugzilla, 42329, "ListView in Frame and FormsAppCompatActivity Memory Leak")] + public class Bugzilla42329 : TestMasterDetailPage + { + const string DestructorMessage = "ContentPageEx Destructor called"; + const string Page1Title = "Page1"; + const string Page2Title = "Page2"; + const string Page3Title = "Page3"; + + protected override void Init () + { + var masterPage = new MasterPage(); + Master = masterPage; + masterPage.ListView.ItemSelected += (sender, e) => + { + var item = e.SelectedItem as MasterPageItem; + if (item != null) + { + Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType)); + masterPage.ListView.SelectedItem = null; + IsPresented = false; + } + }; + + Detail = new NavigationPage(new _42329_FrameWithListView()); + } + + [Preserve(AllMembers = true)] + public class MasterPage : ContentPage + { + public MasterPage() + { + Title = "Menu"; + ListView = new ListView { VerticalOptions = LayoutOptions.FillAndExpand, SeparatorVisibility = SeparatorVisibility.None }; + + ListView.ItemTemplate = new DataTemplate(() => + { + var ic = new ImageCell(); + ic.SetBinding(TextCell.TextProperty, "Title"); + return ic; + }); + + Content = new StackLayout + { + Children = { ListView } + }; + + var masterPageItems = new List(); + masterPageItems.Add(new MasterPageItem + { + Title = Page1Title, + TargetType = typeof(Bugzilla42329._42329_FrameWithListView) + }); + masterPageItems.Add(new MasterPageItem + { + Title = Page2Title, + TargetType = typeof(Bugzilla42329._42329_Page2) + }); + masterPageItems.Add(new MasterPageItem + { + Title = Page3Title, + TargetType = typeof(Bugzilla42329._42329_Page3) + }); + + ListView.ItemsSource = masterPageItems; + } + + public ListView ListView { get; } + } + + [Preserve(AllMembers = true)] + public class MasterPageItem + { + public string IconSource { get; set; } + + public Type TargetType { get; set; } + + public string Title { get; set; } + } + + [Preserve(AllMembers = true)] + public class ContentPageEx : ContentPage + { + ~ContentPageEx() + { + Log.Warning("Bugzilla42329", DestructorMessage); + } + } + + [Preserve(AllMembers = true)] + public class _42329_FrameWithListView : ContentPageEx + { + public _42329_FrameWithListView() + { + var lv = new ListView(); + var label = new Label(); + var frame = new Frame { Content = lv }; + + Title = Page1Title; + Content = new StackLayout + { + Children = { new Label { Text = "Open the drawer menu and select Page2" }, frame } + }; + } + } + + [Preserve(AllMembers = true)] + public class _42329_Page2 : ContentPage + { + public _42329_Page2() + { + Title = Page2Title; + Content = new StackLayout { Children = { new Label { Text = "Open the drawer menu and select Page3" } } }; + } + } + + [Preserve(AllMembers = true)] + public class _42329_Page3 : ContentPage + { + public _42329_Page3() + { + Title = Page3Title; + Content = new StackLayout { Children = { new Label { Text = $"The console should have displayed the text '{DestructorMessage}' at least once. If not, this test has failed." } } }; + } + + protected override void OnAppearing() + { + base.OnAppearing(); + GC.Collect(); + GC.Collect(); + GC.Collect(); + } + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index a98b97dc9..1e3e9f348 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -117,6 +117,7 @@ + diff --git a/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs index b57233ba3..3ca14eaeb 100644 --- a/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs +++ b/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs @@ -138,9 +138,20 @@ namespace Xamarin.Forms.Platform.Android.AppCompat _visualElementPackager.Dispose(); _visualElementPackager = null; } + + int count = ChildCount; + for (var i = 0; i < count; i++) + { + AView child = GetChildAt(i); + child.Dispose(); + } if (Element != null) + { Element.PropertyChanged -= OnElementPropertyChanged; + UnsubscribeGestureRecognizers(Element); + } + } base.Dispose(disposing); @@ -216,7 +227,10 @@ namespace Xamarin.Forms.Platform.Android.AppCompat _collectionChangeHandler = HandleGestureRecognizerCollectionChanged; var observableCollection = (ObservableCollection)view.GestureRecognizers; - observableCollection.CollectionChanged += _collectionChangeHandler; + if (observableCollection != null) + { + observableCollection.CollectionChanged += _collectionChangeHandler; + } } void UnsubscribeGestureRecognizers(VisualElement element) @@ -226,7 +240,10 @@ namespace Xamarin.Forms.Platform.Android.AppCompat return; var observableCollection = (ObservableCollection)view.GestureRecognizers; - observableCollection.CollectionChanged -= _collectionChangeHandler; + if (observableCollection != null) + { + observableCollection.CollectionChanged -= _collectionChangeHandler; + } } void UpdateBackgroundColor()