I made the below CurrentLocationTask
as a way of reducing code duplication, as my application currently has 2 different Activity
s 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"));
}
}
}
1 Answer 1
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.
Explore related questions
See similar questions with these tags.