User Location on Android

Getting the user’s location on Android is a little less straightforward than on iOS. To start the confusion, there are two totally different ways you can do it. The first is using Android APIs from android.location.LocationListener, and the second is using Google Play Services APIs com.google.android.gms.location.LocationListener. Let’s go through both of them.

Android’s Location API

The Android’s location APIs use three different providers to get location —

  1. LocationManager.GPS_PROVIDER — This provider determines location using satellites. Depending on conditions, this provider may take a while to return a location fix.
  2. LocationManager.NETWORK_PROVIDER — This provider determines location based on availability of cell tower and WiFi access points. Results are retrieved by means of a network lookup.
  3. LocationManager.PASSIVE_PROVIDER — This provider will return locations generated by other providers. You passively receive location updates when other applications or services request them without actually requesting the locations yourself.

The gist of it is that you get an object of LocationManager from the system, implement the LocationListener, and call the requestLocationUpdates on the LocationManager. Here’s a code snippet:

LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
    public void onLocationChanged(Location location) {
      // Called when a new location is found by the network location provider.
      makeUseOfNewLocation(location);
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {}

    public void onProviderEnabled(String provider) {}

    public void onProviderDisabled(String provider) {}
  };

// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

Google’s API Guide on Location Strategies explains the code pretty nicely. But they also mention that in most cases, you’ll get better battery performance, as well as more appropriate accuracy, by using the Google Location Services API instead. Now the confusion starts!

Google’s Location Services API

Google’s Location Services API is a part of the Google Play Services APK (here’s how to set it up). They’re built on top of Android’s API. These APIs provide a “Fused Location Provider” instead of the providers mentioned above. This provider automatically chooses what underlying provider to use, based on accuracy, battery usage, etc. It is fast because you get location from a system-wide service that keeps updating it. And you can use more advanced features such as geofencing.

To use the Google’s Location Services, your app needs to connect to the GooglePlayServicesClient. To connect to the client, your activity (or fragment, or so) needs to implement GooglePlayServicesClient.ConnectionCallbacks and GooglePlayServicesClient.OnConnectionFailedListener interfaces. Here’s a sample code:

public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener {
    LocationClient locationClient;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        locationClient = new LocationClient(this, this, this);
    }

    @Override
    public void onConnected(Bundle bundle) {
	Location location = locationClient.getLastLocation() ;
        Toast.makeText(this, "Connected to Google Play Services", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDisconnected() {
	Toast.makeText(this, "Connected from Google Play Services.", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        // code to handle failed connection
        // this code can be found here — http://developer.android.com/training/location/retrieve-current.html 
    }

Why is locationClient.getLastLocation() null?

The locationClient.getLastLocation() gets the last known location from the client. However, the Fused Location Provider will only maintain background location if at least one client is connected to it. Once the first client connects, it will immediately try to get a location. If your activity is the first client to connect and you call getLastLocation() right away in onConnected(), that might not be enough time for the first location to come in. This will result in location being null.

To solve this issue, you have to wait (indeterminately) till the provider gets the location and then call getLastLocation(), which is impossible to know. Another (better) option is to implement the com.google.android.gms.location.LocationListener interface to receive periodic location updates (and switch it off once you get the first update).

public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
    // . . . . . . . . more stuff here 
    LocationRequest locationRequest;
    LocationClient locationClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // . . . . other initialization code
        locationClient = new LocationClient(this, this, this);
	locationRequest = new LocationRequest();
	// Use high accuracy
	locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        // Set the update interval to 5 seconds
	locationRequest.setInterval(UPDATE_INTERVAL);
        // Set the fastest update interval to 1 second
	locationRequest.setFastestInterval(FASTEST_INTERVAL);
    }
    // . . . . . . . . other methods 
    @Override
    public void onConnected(Bundle bundle) {
        Location location = locationClient.getLastLocation();
        if (location == null)
            locationClient.requestLocationUpdates(locationRequest, this);
        else
            Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
    }
    // . . . . . . . . other methods
    @Override
    public void onLocationChanged(Location location) {
        locationClient.removeLocationUpdates(this);
        // Use the location here!!!
    }

In this code, you’re checking if the client already has the last location (in onConnected). If not, you’re requesting for location updates, and switching off the requests (in onLocationChanged() callback) as soon as you get an update.

Note that the locationClient.requestLocationUpdates(locationRequest, this); has to be inside the onConnected callback, or else you will get an IllegalStateException because you will be trying to request for locations without connected to the Google Play Services Client.

User has disabled Location Services

Many times, the user would have location services disabled (to save battery, or privacy reasons). In such a case, the code above will still request for location updates, but onLocationChanged will never get called. You can stop the requests by checking if the user has disabled the location services.

If your app requires them to enable location services, you would want to show a message or a toast. Unfortunately, there is no way of checking if the user has disabled location services in Google’s Location Services API. For this, you will have to resort back to Android’s API.

In your onCreate method:

LocationManager manager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
    locationEnabled = false;
    Toast.makeText(getActivity(), "Enable location services for accurate data", Toast.LENGTH_SHORT).show();
}
else locationEnabled = true;

And use the locationEnabled flag in your onConnected method like this:

if (location != null) {
    Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
}
else if (location == null && locationEnabled) {
    locationClient.requestLocationUpdates(locationRequest, this);
}

I hope this post helped you with your Android location code.

Happy coding!

  • Hannes

    I just would like to tell that your code was a great help.
    I wasted many hours until I found out that the majority of the examples online are built for the opensource locationlistener.
    The example provided by Google for the Google Play Services one is horrible (hundreds of lines of code instead of a proper minimal example)

    Your code was the first one that worked well and uses the new methods.

    • http://www.rahuljiresal.com Rahul Jiresal

      Thanks Hannes! I’m glad it helped.

  • Peter

    I wanted to comment to echo what Hannes said. This is the first tutorial that I’ve actually been able to fully understand and implement. Very nicely distilled to the basics. Than you!

    • http://www.rahuljiresal.com Rahul Jiresal

      Thanks Peter!

  • Dark

    Awesome tutorial. saved my day. Thanks a lot sir!

  • Dark

    Sir, wish you can post something like this for showing paths for multiple destinations in google map (android). waiting for your response.

    • http://www.rahuljiresal.com Rahul Jiresal

      Hi Dark, I’m glad you found the post useful! I have been really busy lately, but I will try to get a post up for plotting multiple points on a map sometime in the coming week. Thank!

  • Gyandeep

    Nice article. But i get error for getActivity(). is it user-defined or an inbuilt function of fragment?

  • Rohitesh Dutta

    Finally a post which explained the difference well! Appreciate it!

  • Rohitesh Dutta

    As I mentioned in my previous comment, really appreciate your post. Also, to add on, I find that adding this bit of code, when User has disabled Location Service :

    // take user to Location Service activity
    intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);

    Hope it helps!

  • Jaichander Ramesh

    Great post Rahul!

    Had a question though, I dont see a call to locationClient.connect() , the docs say that for a callback to happen to onConnected() you need to call connect(). So, is this a bug above or is there something else to it? Additionally, do you recommending disconnecting as soon as I am done with my location needs in the app to save power or is it not such a big deal?

  • Devashish

    Thanks a lot for the awesome article.

  • Himanshu

    I just wanna ask you , I am using this LocationClient class application in nexus 7 tab but in case of network_provider(WIFI connected). I am not able to get location updates.

  • http://about.me/eazyigz Igor Ganapolsky

    LocationClient is now deprecated. Why do you use it?

  • 4mla1fn

    this is a really well done tutorial/explanation. these two location services are confusing and this is the only place i could find that explained them together so well. thanks!

    • http://www.rahuljiresal.com Rahul Jiresal

      Glad it helped!

  • Diego R FG

    The user enters and see his/her location but how can I detect when the user disabled the location Service? remember in phones nowadays is easy just moving the upper panel and disabling the gps button. No method gets called (I am implementing Google play service) In Android API there is a method called onProviderDisabled how can I detect that action?