[Android] Fix ObjectDisposedException on MasterDetailPageRenderer update (#4955)
authorKevin Petit <kevin.petit@outlook.com>
Mon, 18 Feb 2019 11:24:26 +0000 (12:24 +0100)
committerRui Marinho <me@ruimarinho.net>
Mon, 18 Feb 2019 11:24:26 +0000 (11:24 +0000)
* Android - MasterDetailPageRenderer - Fix ObjectDisposedException on update.

* Fix update call to master.

* Move checks inside update methods.
Add HandleMasterPropertyChanged handler unsubscribing.

Xamarin.Forms.Platform.Android/AppCompat/MasterDetailPageRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/MasterDetailRenderer.cs

index 99b6e7b..45b3764 100644 (file)
@@ -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()
index ca66733..234b32b 100644 (file)
@@ -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;
                        }
                }