From 7c72459b29c517fbbce38c14ddeca8c891d31477 Mon Sep 17 00:00:00 2001 From: Kevin Petit Date: Mon, 18 Feb 2019 12:24:26 +0100 Subject: [PATCH] [Android] Fix ObjectDisposedException on MasterDetailPageRenderer update (#4955) * Android - MasterDetailPageRenderer - Fix ObjectDisposedException on update. * Fix update call to master. * Move checks inside update methods. Add HandleMasterPropertyChanged handler unsubscribing. --- .../AppCompat/MasterDetailPageRenderer.cs | 31 +++++++++++++--------- .../Renderers/MasterDetailRenderer.cs | 24 ++++++++++++----- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs index 99b6e7b..45b3764 100644 --- a/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs +++ b/Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs @@ -1,4 +1,3 @@ - using System; using System.ComponentModel; using System.Threading.Tasks; @@ -227,6 +226,9 @@ namespace Xamarin.Forms.Platform.Android.AppCompat if (_masterLayout != null) { + if (_masterLayout.ChildView != null) + _masterLayout.ChildView.PropertyChanged -= HandleMasterPropertyChanged; + RemoveView(_masterLayout); _masterLayout.Dispose(); _masterLayout = null; @@ -387,10 +389,14 @@ namespace Xamarin.Forms.Platform.Android.AppCompat if (_detailLayout.ChildView == null) Update(); else - new Handler(Looper.MainLooper).Post(() => Update()); + // Queue up disposal of the previous renderers after the current layout updates have finished + new Handler(Looper.MainLooper).Post(Update); void Update() { + if (_detailLayout == null || _detailLayout.IsDisposed()) + return; + Context.HideKeyboard(this); _detailLayout.ChildView = Element.Detail; } @@ -412,26 +418,25 @@ namespace Xamarin.Forms.Platform.Android.AppCompat void UpdateMaster() { - if (_masterLayout.ChildView == null) - new Handler(Looper.MainLooper).Post(() => Update()); - else Update(); + else + // Queue up disposal of the previous renderers after the current layout updates have finished + new Handler(Looper.MainLooper).Post(Update); void Update() { - Android.MasterDetailContainer masterContainer = _masterLayout; - if (masterContainer == null) + if (_masterLayout == null || _masterLayout.IsDisposed()) return; - if (masterContainer.ChildView != null) - masterContainer.ChildView.PropertyChanged -= HandleMasterPropertyChanged; + if (_masterLayout.ChildView != null) + _masterLayout.ChildView.PropertyChanged -= HandleMasterPropertyChanged; - masterContainer.ChildView = Element.Master; - if (Element.Master != null) - Element.Master.PropertyChanged += HandleMasterPropertyChanged; - } + _masterLayout.ChildView = Element.Master; + if (_masterLayout.ChildView != null) + _masterLayout.ChildView.PropertyChanged += HandleMasterPropertyChanged; + } } void UpdateSplitViewLayout() diff --git a/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs index ca66733..234b32b 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs @@ -6,7 +6,6 @@ using Android.Content; using Android.Support.V4.Widget; using Android.Views; using AView = Android.Views.View; -using AColor = Android.Graphics.Drawables.ColorDrawable; using Android.OS; using Xamarin.Forms.Platform.Android.FastRenderers; @@ -168,7 +167,7 @@ namespace Xamarin.Forms.Platform.Android element.SendViewInitialized(this); if (element != null && !string.IsNullOrEmpty(element.AutomationId)) - SetAutomationId(element.AutomationId); + SetAutomationId(element.AutomationId); SetContentDescription(); } @@ -209,6 +208,9 @@ namespace Xamarin.Forms.Platform.Android if (_masterLayout != null) { + if (_masterLayout.ChildView != null) + _masterLayout.ChildView.PropertyChanged -= HandleMasterPropertyChanged; + _masterLayout.Dispose(); _masterLayout = null; } @@ -361,10 +363,13 @@ namespace Xamarin.Forms.Platform.Android Update(); else // Queue up disposal of the previous renderers after the current layout updates have finished - new Handler(Looper.MainLooper).Post(() => Update()); + new Handler(Looper.MainLooper).Post(Update); void Update() { + if (_detailLayout == null || _detailLayout.IsDisposed()) + return; + Context.HideKeyboard(this); _detailLayout.ChildView = _page.Detail; } @@ -384,15 +389,20 @@ namespace Xamarin.Forms.Platform.Android Update(); else // Queue up disposal of the previous renderers after the current layout updates have finished - new Handler(Looper.MainLooper).Post(() => Update()); + new Handler(Looper.MainLooper).Post(Update); void Update() { - if (_masterLayout != null && _masterLayout.ChildView != null) + if (_masterLayout == null || _masterLayout.IsDisposed()) + return; + + if (_masterLayout.ChildView != null) _masterLayout.ChildView.PropertyChanged -= HandleMasterPropertyChanged; + _masterLayout.ChildView = _page.Master; - if (_page.Master != null) - _page.Master.PropertyChanged += HandleMasterPropertyChanged; + + if (_masterLayout.ChildView != null) + _masterLayout.ChildView.PropertyChanged += HandleMasterPropertyChanged; } } -- 2.7.4