ThinkGeo.com    |     Documentation    |     Premium Support

ObjectDisposedException on Droid for Map View control

Good Morning.

I have a Xamarin Forms app. I am also using the Shell control to host my pages.
The Shell provides a tab page like interface. I have four pages in the Shell. The issue I am seeing is as I move between these pages and I return to the page hosting your MapView control I get the following.

System.ObjectDisposedException
  Message=Cannot access a disposed object.
Object name: 'ThinkGeo.UI.Android.MapView'.

The page is still in scope as it’s ctor is not fired again and all that is fired are the OnAppearing and onDisappearing methods. I assume you are a little quick to dispose the platforms implementation of the MapView control??

Here are the details of how my Shell is setup.

<FlyoutItem Route="main"
			FlyoutDisplayOptions="AsMultipleItems"
			Title="FlyoutItem">
	<ShellContent Title="Selected Jobs"
				  Icon="Job.png"
				  ContentTemplate="{DataTemplate JobsView:SelectedJobsPage}" />
	<ShellContent Title="Maps"
				  Icon="MapIconSmall.png"
				  ContentTemplate="{DataTemplate map:MapPage}" />
	<ShellContent Title="Chat"
				  Icon="Chat.png"
				  ContentTemplate="{DataTemplate chat:ChatPage}" />
	<ShellContent Title="Browse"
				  Icon="tab_feed.png"
				  ContentTemplate="{DataTemplate localViews:ItemsPage}" />
</FlyoutItem>

The use of ContentTemplate and a DataTemplate for the pages means the pages are Lazy Instantiated. I have switched that out to normal instantiation and it did not change the outcome.

My map page xaml is:

<ContentPage.Content>
	<ThinkGeo:MapView x:Name="mapView"
					  VerticalOptions="FillAndExpand"
					  MapLongPress="mapView_MapLongPress" />
</ContentPage.Content>

and the page is quite normal and is very similar to your samples. I would not expect the MapView control to go out of scope and get disposed when the page it is on is still in scope.

In addition to the above issue I am also seeing the following during the initial MapView setup. As per your samples I am adding Overlays and Layers along with 4 features. This is all being done from the OnAppearing method wrapped in a first time if statement check.

System.ObjectDisposedException
  Message=Cannot access a disposed object.
Object name: 'ThinkGeo.UI.Android.LayerTileView'.

This is not occurring every time but “most of the time”. I cannot see a pattern to the frequency.
I am currently using the OSM overlay if that is important.

This is happening on Android at the moment and I am unable to confirm if it occurring on iOS as I have a pending bug on iOS and unable to test there.

I hope this is enough for you?
If not I am more than happy to provide further details.

Cheers
Chris …

Here are my env details:
Microsoft Visual Studio Professional 2019
Version 16.9.4
VisualStudio.16.Release/16.9.4+31205.134
Microsoft .NET Framework
Version 4.8.04084

Xamarin 16.9.000.273 (d16-9@1bba9e0)
Visual Studio extension to enable development for Xamarin.iOS and Xamarin.Android.

Xamarin Templates 16.9.72 (426ebf6)
Templates for building iOS, Android, and Windows apps with Xamarin and Xamarin.Forms.

Xamarin.Android SDK 11.2.2.1 (d16-9/877f572)
Xamarin.Android Reference Assemblies and MSBuild support.
Mono: 5e9cb6d
Java.Interop: xamarin/java.interop/d16-9@54f8c24
ProGuard: Guardsquare/proguard/v7.0.1@912d149
SQLite: xamarin/sqlite/3.34.1@daff8f4
Xamarin.Android Tools: xamarin/xamarin-android-tools/d16-9@d210f11

Xamarin.iOS and Xamarin.Mac SDK 14.14.2.5 (3836759d4)
Xamarin.iOS and Xamarin.Mac Reference Assemblies and MSBuild support.

ThinkGeo.UI.XamarinForms v12.3.10

Xamarin Forms v4.8.0.1687

Good Morning.

Just wondering about the progress of this issue?
Is there any additional data I can provide to assist?

Cheers
Chris …

Hello.

It has been some time since I posted this question.
I do need some help with this.

Have I posted the question incorrectly?

Regards
Chris …

Chris,

We are working on it and we will be responding to the ticket. The ticket should update to in process or something like that.

Chris,

I think we finally have this fixed. This one was strange because it only happened in Android and only under Shell applications.

The root cause was that in the ViewRenderer we overrided for our map control there is an OnElementChanged which fires when it’s creating the element for the map. The element has a Control property which is where you place the native control, using SetNativeControl, such as the Android or iOS version. The issue was that when you navigated away the element was torn down and the parent disposed. This caused the native control to be disposed as well and the error you reported happened.

The fix was to override the Dispose on the custom ViewRenderer and grab the native control and then remove it from the parent. That way as the parent disposed the native control remained intact and when you navigate back the control is ready to be set back into the new element that gets created.

For some reason iOS did this automatically as the same class for iOS doesn’t need the dispose code and is otherwise functionally identical. I put this code out there in the hope that someone finds this with the same issue. I spent quite a bit of time chasing all kinds of red herrings and pouring over search results before landing on this. I found tons of boilerplate code on the OnElementChanged but nothing about removing it from the parent on Dispose.

We are still testing under other navigation schemes other than Shell and I hope to get this in the NuGet dev channel this weekend.

    public class MapViewRenderer : ViewRenderer<MapView, ThinkGeo.UI.Android.MapView>
    {

        public MapViewRenderer(Context context) : base(context)
        {

        }
        /// <summary>
        /// Called when [element changed].
        /// </summary>
        /// <param name="e">The e.</param>
        protected override void OnElementChanged(ElementChangedEventArgs<MapView> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe from event handlers and cleanup any resources
            }

            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    ThinkGeo.UI.Android.MapView nativeMap = (ThinkGeo.UI.Android.MapView)e.NewElement.Adapter.Concrete;                  
                    SetNativeControl(nativeMap);
                }
               
                // Configure the control and subscribe to event handlers
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                ThinkGeo.UI.Android.MapView nativeMap = (ThinkGeo.UI.Android.MapView)Control;
                nativeMap.RemoveFromParent();
            }
            base.Dispose(disposing);
        }

    }```