Tracking Highly Accurate Location in Android — (Vol.1)

Tracking a user’s location is the core feature of apps such as Nike+, Pokemon Go, and Uber.
I’ve been making a running app from the time of iPhone3.1, and it took a lot of tries and errors to get accurate location information under various environmental condition. As Pokemon Go is getting the people’s attention back to location technology nowadays, I’ll share basic and practical knowledge of location tracking on Android OS.

Make a project

Make a new Android Studio project and make an Empty Activity.

LocationService class

Our aim is tracking user’s location even while the user puts the app in the background.

We use a Service object to do background location tracking.

Let’s start from making a subclass of Service class. We name it as “LocationService” in this project.

Since this LocationService class is not going be used by other apps, uncheck “Exported“ flag.

Now, Android Studio has added a LocationService class to the Manifest.xml file as blow. If you don’t see the class, add the same XML element as below to your Manifest file.

Permissions

In the Manifest file, let us add a few more lines of code.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />

Since we use the location service, we need to add proper permissions. (ACCESS_MOCK_LOCATION is to use dummy GPS location fed from Android emulators, thus you can omit when you release your app.)

Bind and Connect LocationService instance

  1. Start a LocationService within onCreate() of MainActivity.
  2. Bind the service to the MainActivity with a ServiceConnection object which is going to become the listener of binding events between the MainActivity and the LocationService.
  3. Within onServiceConnected() method, obtain the LocationService instance and store it to locationService.
  4. Also implement onServiceDisconnected() method. In this method, assign NULL to locationService since the LocationService object is going to be released.

Now we should get a reference to LocationService object.

LocationManager

Next step is to initialize a LocationManager object in the LocationService class’s implementation.

Create startUpdatingLocation method in LocationService, and within it, initialize a LocationManager object.

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

It’s pretty easy.

Criteria

After instantiating a LocationManager object, you need to setup various parameters. It is a bit tricky.

We must set Criteria properly.

After instantiate a Criteria object,

  • Set the accuracy level to ACCURACY_FINE (for better location)
  • Set the highest power (to get best GPS signal)
  • Don’t request the altitude.
  • Set speedRequired to false since we don’t obtain speed value from locationManger so .
  • Set CostAllowed flag to true. This flag enables LocationManager to exchange “data packet” with 3G/4G network base stations in order to get better location. This additional data packet exchange may increase the cost for the user’s monthly or temporary data plan. If you don’t want to take that risk, you can set it to false or may create an UI to ask users to choose. Since we are interested in getting super high accuracy location information, we set it to true.
  • Set BearingRequired to false. “Bearing” in this context is the direction the device is pointing at/moving in. We don’t use this information this time.
  • Set HorizontalAccuracy and VerticalAccuracy to HIGH.

Below is the Criteria class reference. Check it if you are interested in other options for these parameters.

Then we are going to call requestLocationUpdates with the minimum duration of 1000 milliseconds and the minimum distance of 1 meter, and the criteria we’ve created. 
(gpsFreqInMillis is minimumTime, and gpsFreqInDistance is minimumDistance in the code below.)

Minimum Time and Minimum Distance

The role of minimum duration and distance is a type of filter to avoid your location callback functions from being called too frequent.

Minimum time of 1000 milli seconds means, after getting a location, you are going to get next location from the LocationManager roughly after 1000 milli seconds.

Minimum distance of 1 meter means, after getting a location, you are getting next location after you have moved roughly 1 meter.

These two filters are combined inside the LocationManager in the way you are going to get a location after “one of the” two conditions is filled. This means these two conditions are “OR”ed.

For example, if you are standing up and not moving, new location is obtained after 1 second even though you’ve not moved 1 meter yet.

On the other hand if you are running fast, you are getting a new location after you’ve moved 1 meter even though you’ve moved 1 meter within 0.5 seconds.

Change these values for your purpose.

If you are making an app like Uber, location updates can be less frequent. You may want to set the values to 10 seconds and 10 meters.

and LocationListner (by the 4th parameter of requestLocationUpdates).

LocationListener

Information about the location provider is given via LocationListener interface. We can catch events whether which location provider — Wifi or GPS is currently available. You’ll probably want to update your UI based on this information.

We already registered LocationService as the location listener by passing “this” to the 4th parameter of requestLocationUpdates in the code above.

GPSStatus.Listener

GPSStatusListener receives events as below from the lower level GPS engine.

  • GPS_EVENT_STARTED
  • GPS_EVENT_STOPPED
  • GPS_EVENT_FIRST_FIX
  • GPS_EVENT_SATELLITE_STATUS

My sample code doesn’t use these information, but you can get satellite status information by calling getGpsStatus on the LocationManager object.

To register your class as a GPSStatusListener, call LocationManager’s addGpsStatusListener.

Run the app

Run the app on Android Emulator.

Open Extended controls by clicking “…” on the menu bar of the emulator. 
Here you can send individual GPS data to the emulator.

Here is Extended controls. You can set latitude and longitude values manually and click send button to send a location data to the app.

Or on the bottom, you can click “LOAD GPX/XML” button to load a GPX or KML file. The list of gps data loaded from the GPS or KML file is going to be shown in the table. By clicking the playback button, you can start feeding the location data to the app.

This is pretty convenient when you want to simulate your past run with the emulator.

(The sample projects has a file named “Half_Marathon.gpx” under the top directory. )

On Android Studio’s Logcat console, you will see the value of GPS data received in onLocationChanged().

Sample project is on the repository below
https://github.com/mizutori/AndroidLocationStarterKit

You can switch to the commit “faa85d2f5cacd94f4d9ff005336680f3f9b27891” to see code which only includes what has been explained until here.

In the next post, I will explain how to show the path of GPS waypoints, the user’s current location, and accuracy indicator (a surrounding circle which indicates how accurate the current location information is).

Even when I’m making a location tracking app without map, I always use a map for debugging and performance measurement. 
So I’ll explain about the map in the next post.


I am Tokyo-based Android and iOS app developer. 
If you have any questions about location tracking on Android or iOS, or you are interested in asking us to develop your app. Feel free to contact me anytime.
@mizutory
mizutori@goldrushcomputing.com