Android App Development: How to Get Started with Location

By Michael Burton

Many Android apps can provide a nice benefit to their users by bringing location into their user experiences. Whether it’s something as simple as showing users their location on a map or something more complex such as providing GPS-based background notifications, the addition of location services to your app is often something users expect.

There are many ways you can add location to your Android apps. In Lollipop, some of the most common are

  • Display the user’s current city or street address. This is called reverse geo-coding.

  • Actively track the user’s location, such as when showing the user’s location on a map.

  • Set up a geofence to get a notification when a user arrives or leaves a location.

  • Track users’ location to perform activity recognition and determine whether they are walking, bicycling, or driving.

  • Detect whether a user is near an iBeacon using Bluetooth.

This article shows you one of the most common uses of location, which is to monitor users’ location and display their current street address to them in a TextView.

You’ll need to do four things to set up locations in your app:

  1. Update your build configuration.

  2. Open a connection to Google Play.

  3. Subscribe to location updates.

  4. Display the user’s address.

How to update your build configuration

Android has an older set of Location APIs built-in to the OS under the android.location package. These APIs are no longer maintained, so you should use the new Google Play Services APIs to get the user’s location instead.

To add Google Play Services Location API to your project, place the following dependency into your build.config file:

dependencies {
  compile 
    'com.google.android.gms:play-services-location:6.5.87'
}

Next, add the following to your AndroidManifest.xml:

<uses-permission android:name
    ="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name
    ="android.permission.ACCESS_FINE_LOCATION"/>
<application …>
  <meta-data
    android:name="com.google.android.gms.version"
    android:value
      ="@integer/google_play_services_version"/>
</application>

The ACCESS_COARSE_LOCATION permission uses Wi-Fi and cell tower information, when available, to provide location updates roughly on the size of a city block. The ACCESS_FINE_LOCATION permission can get you much higher-resolution information using the phone’s GPS, but at the expense of greater battery consumption. If your app needs only coarse location, you might think it makes sense to request only the ACCESS_COARSE_LOCATION permission, but really most apps will want both permissions.

You should usually request both permissions because you will be using a LocationProvider called the FusedLocationProvider, which automatically chooses between Wi-Fi, cell tower, and GPS location providers to give you the best accuracy at the lowest cost to your battery. If you know you will never need fine location information, you can consider removing the ACCESS_FINE_LOCATION permission, but be aware that your app may not work well in places that do not have cell tower or Wi-Fi access.

The meta-data tag is necessary whenever you use the Google Play Service library.

How to open a connection to Google Play

The Location APIs are quite simple, but to use them you’ll need to set up the Google Play Services APIs, and those are a little more complicated to use.

To establish a connection to the Google Play Services, add the following to your activity or fragment:

// The Google API client, for location services
  GoogleApiClient googleApiClient;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    googleApiClient 
        = new GoogleApiClient.Builder(getActivity())
      .addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this)
      .addApi(LocationServices.API)
      .build();  
    …
  }
  @Override
  public void onStart() {
    super.onStart();
    googleApiClient.connect();
  }
  @Override
  public void onStop() {
    googleApiClient.disconnect();
    super.onStop();
  }

This code creates a new GoogleApiClient object in your onCreate() to connect to Google Play Services, and then it tries to connect in your onStart() and disconnect in your onStop(). The GoogleApiClient needs a callback object that it can inform when a connection has been established, so you need to add callbacks to your activity or fragment. You can add the callbacks to the TaskEditFragment to any activity of fragment.

public class TaskEditFragment extends Fragment implements
    ConnectionCallbacks, OnConnectionFailedListener
{
  @Override
  public void onConnected(Bundle connectionHint) {
    // Connected to Google Play services!
    // This is where you will start asking for location
    // updates.
  }
  @Override
  public void onConnectionSuspended(int cause) {
    // The Google Play connection has been interrupted.
    // Disable any UI components that depend on Google 
    // APIs until onConnected() is called.
    //
    // This example doesn't need to do anything here.
  }
  @Override
  public void onConnectionFailed(ConnectionResult result) 
  {
    // This callback is important for handling errors 
    // that may occur while attempting to connect with 
    // Google.
    //
    // If your app won't work properly without location
    // updates, then it's important to handle connection 
    // errors properly. See
    // https://developer.android.com/google/auth/api-client.html
    // for more information about handling errors when 
    // connecting to Google.
    //
    // Location information is optional for this app, so 
    // connection errors can be safely ignored here.
  }
}

Congratulations! You’ve now properly set up a connection to Google Play Services which will automatically disconnect when your activity or fragment is stopped. The next step is to request location updates.

How to subscribe to location updates

You’ve gotten through the hard part. Once you have a successful connection to Google Play Services, you can start listening for location updates.

You first need to tell the Location API how often and how accurate you want your location updates to be. If you must display users’ current location to them on a map, perhaps you will want very accurate and rapid updates, so that users can see where they are as they move around. Most apps probably need less frequent updates, and that’s what you’ll use in this example.

Add the following field to your fragment or activity:

// The LocationRequest defines how often we need location 
// updates
LocationRequest locationRequest = new LocationRequest()
  .setInterval(30000)
  .setPriority(
      LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

This locationRequest is set up to ask for new location updates approximately every 30 seconds, and to try to balance good accuracy with decent power consumption. If you want lower battery consumption, try PRIORITY_LOW_POWER, or if you want higher accuracy, try PRIORITY_HIGH_ACCURACY. You can even use PRIORITY_NO_POWER for apps that update very infrequently in the background and just want to piggy-back off the location updates that other apps request! Check out LocationRequest on the Android Developer site for more information about the various options you can use.

To start listening for location updates, add the following to the onConnected() method you just created:

@Override
  public void onConnected(Bundle connectionHint) {
    // Connected to Google Play services!
    // Start subscribing to location updates
    FusedLocationApi.requestLocationUpdates(
      googleApiClient,
      locationRequest, this);
  }

This asks the FusedLocationApi to start sending your app location updates using the LocationRequest you just configured. For it to work, though, you need to provide a callback object that can receive the location updates. Add the LocationListener interface to your activity or fragment, and then add the onLocationChanged() method like so:

public class TaskEditFragment extends Fragment implements
    ConnectionCallbacks, OnConnectionFailedListener,
    com.google.android.gms.location.LocationListener
{
  @Override
  public void onLocationChanged(final Location location) 
  {
    Toast.makeText(getActivity(),location.toString(),
      Toast.LENGTH_LONG).show();
  }
}

By the way, if all you care about is the user’s current location, and you don’t care about keeping it updated, check out FusedLocationApi.getLastLocation(), which removes the need for the LocationListener interface.

Now, if you run your activity, you should see a Toast message which prints out the user’s current location.

image0.png

Being able to see their GPS coordinate isn’t really all that useful to users by itself, though, so let’s use their location to figure out their current address and display it to them.

How to display the user’s address

Android has a built-in API called the Geocoder for looking up a user’s address from his or her GPS coordinates (and vice versa).

It’s pretty simple to use. The only catch is that because it makes a network request to Google on the current thread, it’s up to you to make sure you only run it on the background thread (so that you don’t cause a hang on the main UI thread on slow networks).

Add the following to your activity or fragment:

Geocoder geocoder;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    …
    geocoder = new Geocoder(getActivity());
  }
  @Override
  public void onLocationChanged(final Location location) 
  {
    // Ask google for the address of the location.
    // This may take awhile, so do it in an AsyncTask.
    new AsyncTask<Void,Void,Address>() {
      @Override
      protected Address doInBackground(Void[] params) {
        try {
          List<Address> addresses 
              = geocoder.getFromLocation(
                  location.getLatitude(),
                  location.getLongitude(), 1);
          if( addresses!=null && addresses.size()>0 )
            return addresses.get(0);
         
        } catch (IOException e) {
          // ignored
        }
        return null;
      }
      // Performed on the UI thread
      @Override
      protected void onPostExecute(Address address) {
        if(address!=null) {
          String s = address.getThoroughfare();
          if (s != null)
            Toast.makeText(getActivity(),s,
              Toast.LENGTH_LONG).show();
        }
      }
    }.execute();
  }

This code adds a Geocoder field to your activity or fragment, initializes it in onCreate(), and then uses it in onLocationChanged() to find the address for the current location. It then displays a Toast message with the “thoroughfare” for the address—for example, “600 West Chicago Avenue.” The Address object returned by the Geocoder is rich with information; check out the javadocs for details on what else you can display.

And with that, you have just about everything you need to know about subscribing to the user’s current location from within an Android activity or fragment.