Tutorial: Android Location Service Example



Hi folks,

This tutorial will help you to start using location services (in particular: LocationManager class to get user location and Geocoder to translate location into addresses) and Google Maps on Android.

At the end of this tutorial, you will have a working application that get user location over time, show the approximative address to the user and his location on a map.

1) Start A New Project
The first step is to create a new project
- Click on File -> New -> Android Project
- Give the project a name. I name the project "MarakanaMaps"
- Choose the location you want for your project (it can be in workspace or in a place of your choice)
- Select a Build Target. Make sure that you choose one that has the Google APIs. I choose the following:
Build target

- Then, finish creating the project by filling out the Properties section like this:
Properties section

NOTE: Make sure that you let Create Activity checked since we are going to have an activity containing user location information along with its position on a map

 


- Click on Finish

2) Create The UI For Our Activity
- By default, when we choose to create the activity, the ADT plugin creates the activity class along with its XML layout file.
- To build our layout, let me first show you what the application is going to look like:
Final application

- The layout is composed of a general LinearLayout containing a TextView and a MapView
- Change the layout to be like this one:

- res/layout/main.xml

 

 

Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
  
  
  <TextView android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Waiting for location..."
    android:id="@+id/lblLocationInfo"/>
  <!-- <1> -->
  
  
      
  <com.google.android.maps.MapView
      xmlns:android="https://schemas.android.com/apk/res/android"
      android:id="@+id/mapview"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:clickable="true"
      android:apiKey="YOUR_API_KEY_GOES_HERE"/>
  <!-- <2> -->
    
</LinearLayout>


- 1: We give an id to the TextView since we will manipulate its text value later in the code
- 2: Same drill here, we give an id to the MapView so it can be accessed later in the code. Finally since we use Google Maps data, we have to register with the Google Maps service and agree to the applicable ToS so our MapView will be able to obtain data from Google Maps.


2.1) Obtain The API Key
- For the purpose of this tutorial we can register with the MD5 fingerprint of the Android SDK debug certificate
- To get the MD5 fingerprint of the Android SDK debug certificate, execute the following command:

 

 

Code:
keytool -list -alias androiddebugkey \
-keystore <path_to_debug_keystore>.keystore \ <1>
-storepass android -keypass android


- 1: Depending on the OS you are developing with, the path to the keystore can be located at the following location:
Windows Vista / Windows 7: C:\Users\<user>\.android\debug.keystore
Windows XP: C:\Documents and Settings\<user>\.android\debug.keystore
Mac OS X / Linux: ~/.android/debug.keystore

- Now, go to the following URL: https://code.google.com/android/maps-api-signup.html and copy/paste the MD5 previously obtained to then get the API key to use.
- Copy paste this API key in the MapView of the res/layout/main.xml layout file and then this is it for the layout, we are not going to modify it anymore!

3) Modify The AndroidManifest.xml
- We need to declare in the manifest that we are using the Google Maps library. Add the following as a direct child of the `<application>` element:

- AndroidManifest.xml

 

 

Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        ...
        <uses-library android:name="com.google.android.maps" /> <1>
    </application>
</manifest>


- 1: This declares that we are using the Google Maps library

- Since the tiles for the MapView are loaded from the internet we need to give our application the INTERNET permission like this in the <manifest> element:

- AndroidManifest.xml

 

 

Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <uses-permission android:name="android.permission.INTERNET" /> <1>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <2>
    ...
</manifest>


- 1: The application now have the right to open network sockets
- 2: This allows the application to use fine location sensors (GPS...)


4) Run The Application For The First Time
- We are done with the manifest file for the moment. Let us run the application so we can see if the TextView and the MapView are correctly displayed
- To do so, right-click on your project -> Run -> Run

 

 

 

NOTE: In order to run your application, you must have an emulator supporting the Google APIs with the right SDK version (SDK Version 4 in our case).

 


- The application fails to start and we get an exception. To find out what has just happened, you can view the log statements using the adb logcat in a terminal and here we are looking for the following information:

 

 

 

 

Code:
E/AndroidRuntime(  516): Caused by: java.lang.IllegalArgumentException: MapViews can only be created inside instances of MapActivity.

 


- Actually, when we created our activity, the ADT plugin just made inherit from the Activity class like this.

LocationActivity.java

 

 

 

Code:
public class LocationActivity extends Activity

 


- We now make the changes so it extends from MapActivity:

LocationActivity.java

 

 

 

Code:
public class LocationActivity extends MapActivity

 


- Eclipse is going to complain if you do not override some methods. Here for the MapActivity, there is only one method to override:

LocationActivity.java

 

 

 

Code:
package com.marakana.tutomaps;

import android.os.Bundle;

import com.google.android.maps.MapActivity;

public class LocationActivity extends MapActivity {
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  }

  @Override
  protected boolean isRouteDisplayed() {
    // TODO Auto-generated method stub
    return false;
  }
}

 


- Now we can run the application again and see the following result:
First Launch

5) Getting The User Location
In this section:
- We want to be able to get user location information such as latitude and longitude.
- We do not display yet a marker on the map but we set the center of the map to be the position of the user.

Below is the code of the LocationActivity class to get user location on Android.
Package statement, import statements and overridden methods that we do not modify are not shown here to keep the code simple:

LocationActivity.java

 

 

 

Code:
...
public class LocationActivity extends MapActivity implements LocationListener { //<1>
  
  private static final String TAG = "LocationActivity";

  LocationManager locationManager; //<2>
  Geocoder geocoder; //<3>
  TextView locationText;
  MapView map;  
  MapController mapController; //<4>
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    locationText = (TextView)this.findViewById(R.id.lblLocationInfo);
    map = (MapView)this.findViewById(R.id.mapview);
    map.setBuiltInZoomControls(true);
    
    mapController = map.getController(); //<4>
    mapController.setZoom(16);
    
    locationManager = (LocationManager)this.getSystemService(LOCATION_SERVICE); //<2>
    
    geocoder = new Geocoder(this); //<3>
    
    Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); //<5>
    if (location != null) {
      Log.d(TAG, location.toString());
      this.onLocationChanged(location); //<6>
    }
  }

  @Override
  protected void onResume() {
    super.onResume();
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 10, this); //<7>
  }

  @Override
  protected void onPause() {
    super.onPause();
    locationManager.removeUpdates(this); //<8>
  }

  @Override
  public void onLocationChanged(Location location) { //<9>
    Log.d(TAG, "onLocationChanged with location " + location.toString());
    String text = String.format("Lat:\t %f\nLong:\t %f\nAlt:\t %f\nBearing:\t %f", location.getLatitude(), 
                  location.getLongitude(), location.getAltitude(), location.getBearing());
    this.locationText.setText(text);
    
    try {
      List<Address> addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 10); //<10>
      for (Address address : addresses) {
        this.locationText.append("\n" + address.getAddressLine(0));
      }
      
      int latitude = (int)(location.getLatitude() * 1000000);
      int longitude = (int)(location.getLongitude() * 1000000);

      GeoPoint point = new GeoPoint(latitude,longitude);
      mapController.animateTo(point); //<11>
      
    } catch (IOException e) {
      Log.e("LocateMe", "Could not get Geocoder data", e);
    }
  }

  ...
}


- 1: Our class extends the MapActivity class so it can contain MapViews and it also implements the LocationListener interface to contain callbacks that are called when the LocationManager receives new location updates
- 2: The LocationManager provides access to system location services
- 3: The Geocoder class handles geocoding (transforms an address into lat,long coordinate) and reverse geocoding (gets an address from a latitude,longitude coordinate). In our case, we just use it to do reverse geocoding
- 4: The MapController will be useful to set the zoom level on the map. It will also be used to move the map center point to the current user location
- 5: When the activity starts, it executes the onCreate method. Getting the first user location is often time very long to the user so you might want to display a cached location using the getLastKnownLocation method
- 6: If this method returns a location which is not null, we want to process it so we call onLocationChanged method (overridden in our activity)
- 7: In the onResume method, we request location updates by specifying what is the provider for our locations, the minimum time between notifications in milliseconds, the minimum distance in meters for notifications and finally which LocationListener to use to process new updates
- 8: In the onPause method (in other words, when the activity is not visible on the screen), we do not want to drain battery so we do not listen for location updates anymore. When the activity becomes visible again, the onResume method is called so we now listen for location updates again
- 9: The overridden onLocationChanged method is called when the device receives a new location (with a Location parameter). This is where we convert the location into an address
- 10: The getFromLocation method converts a coordinate (latitude,longitude) into a list of approximative addresses around that coordinate
- 11: This moves the center of the map to the obtained location


6) Try Out The Application
If you start your application in the emulator, you will not be able to see location updates since the emulator is not a physical device. However, there is a way to send location updates to your emulator like this:

- Connect to your emulator like this:

 

 

Code:
telnet localhost 5554

 


- You will be able to send location updates like this:

 

 

 

Code:
geo fix <longitude> <latitude>

 


- For instance if you send the following command:

 

 

 

Code:
geo fix -122.41914 37.77919

 


You will get the following result:
Final app city hall

The final project application can be downloaded here

 

 

Published April 28, 2011