Making SupportMapFragment load faster (Xamarin.Android) / by Pranav Khandelwal

So I am working on an Android application in which one of the sections is a location search screen. Because I need backwards compatibility, I am using the SupportMapFragment to load the map into the layout.

The layout is fairly complex, consisting of a parent Activity, with tabs at the bottom. Each tab is represented by a Fragment, and each of these Fragments can contain a ViewPager that displays several other Fragments. For reasons that are irrelevant to this post, I needed to init and inject the SupportMapFragment into the layout programmatically. 

One of the issues I was running into was that when I went to the Locations tab, I would see an ugly black background where the map is supposed to be and after about 3-5 seconds, the screen would update to display the map. This is not ideal at all, and a jarring user experience. So off to StackOverflow I went, looking for answers to a problem that I suspected many people have encountered before. My suspicions were confirmed:

http://stackoverflow.com/questions/26265526/what-makes-my-map-fragment-loading-slow
http://stackoverflow.com/questions/26178212/first-launch-of-activity-with-google-maps-is-very-slow
http://stackoverflow.com/questions/26977051/supportmapfragment-loads-extremely-slowly-the-first-time-but-quickly-in-subsequ

So the natural question was, why is it taking so long to load the map? After doing some research and a bit of debugging, I realized that downloading everything that is needed to display the map from the Google Play Services API was the primary issue. 

The Solution (Hack?):

I hate to call this a solution because it feels more like a hack, but whatever, lets just get to the nitty gritty. I basically knew that I needed to download all the data from Google Play Services before the user is able to access the Locations tab. The app has an initial Activity (lets call it InitActivity) that is loaded with a spinner while it downloads the necessary data to launch...kind of like the Splash Screen on iOS apps. This seemed like a pretty ideal spot to download Google Play Services stuff for the map.

So what I did was in the OnCreate method of the InitActivity, I constructed a SupportMapFragment, injected it into the layout and dispatched the call to download the Google Play Services data. 

protected override void OnCreate(Bundle bundle)
{   
    base.OnCreate(bundle);

    SetContentView(Resource.Layout.init);

    var supportMapFragment = SupportMapFragment.NewInstance();
    var supportMapFragment.GetMapAsync(this);

    var ft = SupportFragmentManager.BeginTransaction();
    ft.Add(Resource.Id.pre_load_map_frame, supportMapFragment, typeof(SupportMapFragment).Name);
    ft.Commit();
}

public void OnMapReady(GoogleMap googleMap)
{
}

Notice that the OnMapReady callback method is completely empty...because we don't use this map in anyway. Another thing to note here is that my pre_load_map_frame ViewGroup in which I am injecting the SupportMapFragment is actually just a FrameLayout with a height of 0dp.

I also learned that we have to perform the Fragment Transaction if we want the GetMapAsync call to actually download the Google Play Services data. When I tried removing the transaction as an optimization, I noticed that the OnMapReady callback stopped firing completely, suggesting that the data wasn't being downloaded! 

Once this runs, anytime in the future when I try to construct and inject a SupportMapFragment to a layout, it shows up instantly. This works because the SupportMapFragment class will cache the downloaded Google Play Services data after the initial download. 

The downside of this approach is that the 3-5 second loading time hasn't been eliminated, but instead just moved to the launch time of the app....which isn't ideal, but it is leaps and bounds better than the nasty, jarring user-experience of loading the map on the Location tab. 

-PK