0
\$\begingroup\$

I made the below CurrentLocationTask as a way of reducing code duplication, as my application currently has 2 different Activitys which both use GoogleApiClient and OnLocationChanged.

The task basically connects a GoogleApiClient, then uses a LocationRequest to subscribe to RequestLocationUpdates.

In the OnLocationChanged method, CurrentLocation is updated, and the PropertyChanged event is fired.

Because the PropertyChangedEventHandler is public, it can be subscribed to from the required Activity. So the task can be called from an Activity on which we need to get the user's current location.

Usage

OnCreate

Create a new CurrentLocationTask and Execute, passing in the current activity as context. Then subscribe to the PropertyChanged event with OnCurrentLocationChanged:

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    SetContentView(Resource.Layout.Main);

    // Subscribe to current location
    _currentLocationTask = new CurrentLocationTask();
    _currentLocationTask.Execute(this);
    _currentLocationTask.PropertyChanged += OnCurrentLocationChanged;
}

OnCurrentLocationChanged

We can get CurrentLocation from the CurrentLocationTask:

private void OnCurrentLocationChanged(object sender, PropertyChangedEventArgs e)
{
    _currentLocation = _currentLocationTask.CurrentLocation;
}

OnStop

In OnStop, Disconnect the GoogleApiClient:

protected override void OnStop()
{
    base.OnStop();
    _currentLocationTask.Client.Disconnect();
}

The code for the AsyncTask is below. Any recommendations are much appreciated.

I'm not sure if using AsyncTask is the best way of doing this, especially since I don't think very much of the code here is actually being called in RunInBackground.

CurrentLocationTask

using System.Linq;
using System.ComponentModel;
using Android.OS;
using Android.Gms.Common.Apis;
using Android.Gms.Common;
using Android.Gms.Location;
using Android.Locations;
using Android.Content;

namespace WeatherApp.Droid
{
    class CurrentLocationTask : 
        AsyncTask<Context, int, Java.Lang.Void>,
        GoogleApiClient.IConnectionCallbacks,
        GoogleApiClient.IOnConnectionFailedListener,
        Android.Gms.Location.ILocationListener,
        INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public Location CurrentLocation { get; set; }
        public GoogleApiClient Client;

        private LocationRequest _locationRequest;
        private Context _context;

        // AsyncTask
        protected override Java.Lang.Void RunInBackground(params 
            Context[] context)
        {
            _context = context.First();
            // Build the client and connect
            Client = 
                new GoogleApiClient.Builder(_context)
                .AddApi(LocationServices.API)
                .AddConnectionCallbacks(this)
                .Build();

            Client.Connect();

            return null;
        }

        // API connection
        public void OnConnected(Bundle connectionHint)
        {
            _locationRequest = LocationRequest.Create();
            _locationRequest.SetPriority(LocationRequest.PriorityHighAccuracy);
            _locationRequest.SetInterval(1000);

            LocationServices.FusedLocationApi.RequestLocationUpdates(
                Client, _locationRequest, this);
        }
        public void OnConnectionFailed(ConnectionResult result)
        {
            MessageBox.Show(_context, "Connection Failed");
        }
        public void OnConnectionSuspended(int cause)
        {
            MessageBox.Show(_context, "Connection Suspended");
        }

        // Location changed
        public void OnLocationChanged(Location location)
        {
            CurrentLocation = location;

            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs("CurrentLocation"));
        }
    }
}
\$\endgroup\$
2
\$\begingroup\$

Note: the following refers to native Android development, I am not familiar with xamarin.

About the approach It depends on what your aim is. The downside of using AsyncTask is that it's tied to the Activity you're calling it from. I recently implemented continuous location tracking, where AsyncTasks were not an option, as the process of continuous tracking is not tied to a particular activity. So I opted for an Android Service implementation instead. The Service was in it's own class and while you could bind to a service in a given Activity I instead went for the EventBus pattern (there are other options though) - meaning every time the LocationService obtained a new location it would cache the most recent location and propagate an Event via the Eventbus. This way subscribers (such as Activities) would be informed of location changes and could update the UI accordingly.

However, if you only want to query for locations within the Map Activity, then an AsyncTask may in fact be enough (I would put that in a separate class file though so it's reusable). The downside of this approach is, that since you only start asking for locations once you enter the Map view, it may take a moment to obtain a viable location.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.