dotnet / android

.NET for Android provides open-source bindings of the Android SDK for use with .NET managed languages such as C#
MIT License
1.93k stars 531 forks source link

System.ObjectDisposedException from MasterDetailPageRenderer #2604

Closed daveford closed 5 years ago

daveford commented 5 years ago

I am getting a System.ObjectDisposedException from Xamarin code. I have not been able to have any of my code break when the error is thrown.

My Code

From my Menu

public MenuPage(RootPage root)
        {
            this.root = root;
            InitializeComponent ();
            Xamarin.Forms.NavigationPage.SetHasNavigationBar(this, false);
            BindingContext = new BaseViewModel(Navigation)
            {
                Title = "Pabst Survey",
                Subtitle = "Pabst Survey",
                Icon = "iconMenu32.png"
            };

            RefreshCommand = new Command(async () => await ExecuteRefreshCommand());
            RefreshCommand.Execute(null);

            ListViewMenu.ItemsSource = menuItems = new List<HomeMenuItem>
                {
                    new HomeMenuItem { Title = "Stores", MenuType = MenuType.Home },
                    new HomeMenuItem { Title = "My Profile", MenuType = MenuType.MyProfile },
                    //new HomeMenuItem { Title = "Scoreboard", MenuType = MenuType.Scoreboard },
                    new HomeMenuItem { Title = "Settings", MenuType = MenuType.Settings },
                    new HomeMenuItem { Title = "Help", MenuType = MenuType.Help },
                    new HomeMenuItem { Title = "Legal", MenuType = MenuType.Legal },
                    new HomeMenuItem { Title = "Logout", MenuType = MenuType.Logout },
                };

            ListViewMenu.SelectedItem = menuItems[0];

            ListViewMenu.ItemSelected += async (sender, e) =>
            {
                if (ListViewMenu.SelectedItem == null)
                    return;

                await this.root.NavigateAsync(((HomeMenuItem)e.SelectedItem).MenuType);
            };
        }

From my RootPage

public async Task NavigateAsync(MenuType id)
        {

            Page newPage;
            if (!Pages.ContainsKey(id))
            {
                switch (id)
                {
                    case MenuType.Home:
                        var page = new SurveyNavigationPage(new HomePage
                        {
                            Title = "Stores & Surveys",
                            Icon = new FileImageSource { File = "stores.png" }
                        });
                        Pages.Add(id, page);
                        break;
                    case MenuType.MyProfile:
                        page = new SurveyNavigationPage(new MyProfilePage
                        {
                            Title = "My Profile",
                            Icon = new FileImageSource { File = "myprofile.png" }
                        });
                        Pages.Add(id, page);
                        break;
                    //case MenuType.Scoreboard:
                        //page = new DFWSNavigationPage(new ScoreboardPage
                        //{
                            //Title = "Scoreboard",
                            //Icon = new FileImageSource { File = "scoreboard.png" }
                        //});
                        //Pages.Add(id, page);
                        //break;
                    case MenuType.Settings:
                        page = new SurveyNavigationPage(new SettingsPage
                        {
                            Title = "Settings",
                            Icon = new FileImageSource { File = "settings.png" }
                        });
                        Pages.Add(id, page);
                        break;
                    case MenuType.Help:
                        page = new SurveyNavigationPage(new HelpPage()
                        {
                            Title = "Help",
                            Icon = new FileImageSource { File = "help.png" }
                        });
                        Pages.Add(id, page);
                        break;
                    case MenuType.Legal:
                        page = new SurveyNavigationPage(new LegalPage()
                        {
                            Title = "Legal",
                            Icon = new FileImageSource { File = "legal.png" }
                        });
                        Pages.Add(id, page);
                        break;
                    case MenuType.Logout:
                        page = new SurveyNavigationPage(new LogoutPage()
                        {
                            Title = "Logout",
                            Icon = new FileImageSource { File = "logout.png" }
                        });
                        Pages.Add(id, page);
                        break;
                }
            }

            newPage = Pages[id];
            if (newPage == null)
                return;

            Detail = newPage;

            if (Device.Idiom != TargetIdiom.Tablet)
                IsPresented = false;
        }

Logout Page code behind

public partial class LogoutPage : ContentPage
    {
        public LogoutPage ()
        {
            InitializeComponent();
            Application.Current.MainPage = new NavigationPage(new LoginPage());
        }
    }

Login Page Code behind

public partial class LoginPage : ContentPage
    {
        public LoginPage ()
        {
            InitializeComponent ();
            BindingContext = new ViewModels.LoginPageViewModel();
            Xamarin.Forms.NavigationPage.SetHasNavigationBar(this, false);
        }
    }

Custom button renderer

public class FlatButtonRenderer : ButtonRenderer
    {
        public FlatButtonRenderer(Context context) : base(context)
        {
            AutoPackage = false;
        }

        protected override void OnDraw(Canvas canvas)
        {
            base.OnDraw(canvas);
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            try
            {
                base.OnElementChanged(e);
            }
            catch(ObjectDisposedException ex)
            {
                Dictionary<string, string> props = new Dictionary<string, string>
                {
                    {"Action", "Button Changed" }
                };
                Crashes.TrackError(ex, props);
            }
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }

Steps to Reproduce

  1. I login from the Login screen.
  2. Open the menu and click Logout
  3. Crash When I click Logout the ItemSelected code runs and this line, await this.root.NavigateAsync(((HomeMenuItem)e.SelectedItem).MenuType); The I can step my code through until it creates the custom buttons and executes the OnElementChanged function for all buttons on the Login screen. After the 4th button executes the OnElementChanged method, the error is thrown.

Expected Behavior

The expected behavior is that the Login screen is set as the current screen.

Actual Behavior

Before the Login screen is displayed the app crashes.

Version Information

Xamarin.Forms v4.0.0.8055-pre1

Log File

01-10 13:25:49.614 I/MonoDroid( 4928): UNHANDLED EXCEPTION: 01-10 13:25:49.625 I/MonoDroid( 4928): System.ObjectDisposedException: Cannot access a disposed object. 01-10 13:25:49.625 I/MonoDroid( 4928): Object name: 'Xamarin.Forms.Platform.Android.AppCompat.MasterDetailPageRenderer'. 01-10 13:25:49.625 I/MonoDroid( 4928): at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00029] in <42dc777b518744fdae9988e94489a4a0>:0 01-10 13:25:49.625 I/MonoDroid( 4928): at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue parameters) [0x00000] in <42dc777b518744fdae9988e94489a4a0>:0 01-10 13:25:49.625 I/MonoDroid( 4928): at Android.Views.View.get_Context () [0x0000a] in <1219ce5aae934ab095dc0e05b2110050>:0 01-10 13:25:49.625 I/MonoDroid( 4928): at Xamarin.Forms.Platform.Android.AppCompat.MasterDetailPageRenderer.g__Update|75_1 () [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.Android\AppCompat\MasterDetailPageRenderer.cs:382 01-10 13:25:49.625 I/MonoDroid( 4928): at Xamarin.Forms.Platform.Android.AppCompat.MasterDetailPageRenderer.b75_0 () [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.Android\AppCompat\MasterDetailPageRenderer.cs:378 01-10 13:25:49.626 I/MonoDroid( 4928): at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <1219ce5aae934ab095dc0e05b2110050>:0 01-10 13:25:49.626 I/MonoDroid( 4928): at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr nativethis) [0x00009] in <1219ce5aae934ab095dc0e05b2110050>:0 01-10 13:25:49.626 I/MonoDroid( 4928): at (wrapper dynamic-method) System.Object.27(intptr,intptr) 01-10 13:25:49.640 E/AppCenterCrashes( 4928): Unhandled Exception from source=AndroidEnvironment 01-10 13:25:49.640 E/AppCenterCrashes( 4928): System.ObjectDisposedException: Cannot access a disposed object. 01-10 13:25:49.640 E/AppCenterCrashes( 4928): Object name: 'Xamarin.Forms.Platform.Android.AppCompat.MasterDetailPageRenderer'. 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Java.Interop.JniPeerMembers.AssertSelf (Java.Interop.IJavaPeerable self) [0x00029] in <42dc777b518744fdae9988e94489a4a0>:0 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue parameters) [0x00000] in <42dc777b518744fdae9988e94489a4a0>:0 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Android.Views.View.get_Context () [0x0000a] in <1219ce5aae934ab095dc0e05b2110050>:0 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Xamarin.Forms.Platform.Android.AppCompat.MasterDetailPageRenderer.g__Update|75_1 () [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.Android\AppCompat\MasterDetailPageRenderer.cs:382 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Xamarin.Forms.Platform.Android.AppCompat.MasterDetailPageRenderer.b75_0 () [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.Android\AppCompat\MasterDetailPageRenderer.cs:378 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <1219ce5aae934ab095dc0e05b2110050>:0 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr nativethis) [0x00009] in <1219ce5aae934ab095dc0e05b2110050>:0 01-10 13:25:49.640 E/AppCenterCrashes( 4928): at (wrapper dynamic-method) System.Object.27(intptr,intptr) 01-10 13:25:49.655 D/Mono ( 4928): Assembly Ref addref Microsoft.AppCenter.Crashes[0xa1654e60] -> System.Core[0x922b9c80]: 16 01-10 13:25:50.092 W/art ( 4928): JNI RegisterNativeMethods: attempt to register 0 native methods for android.runtime.JavaProxyThrowable 01-10 13:25:50.109 D/Mono ( 4928): DllImport searching in: '__Internal' ('(null)'). 01-10 13:25:50.109 D/Mono ( 4928): Searching for 'java_interop_jnienv_throw'. 01-10 13:25:50.109 D/Mono ( 4928): Probing 'java_interop_jnienv_throw'. 01-10 13:25:50.109 D/Mono ( 4928): Found as 'java_interop_jnienv_throw'.

kvpt commented 5 years ago

From your stacktrace, several things :

kvpt commented 5 years ago

I also encounter this issue myself. I made a PR here which I think will fix the issue.