Overview

Android: GPS positioning and location strategies

24 Comments

Note: This blog post is no longer maintained because it is outdated. Any questions and comments will not be answered.

Have you ever wondered how easy or hard it is to implement GPS tracking for your Android application?
Do you know which are the challenges of balancing between best accuracy, power consumption and acquisition speed when acquiring a location?

Obtaining an accurate user location on a mobile device can be difficult. Generally there are three concerns that will challenge you the most:

  1. Multiple location sources – There is more then one provider from which location is acquired (GPS, WI-FI and Cell-ID) and they all vary very much in accuracy, power consumption and speed.
  2. User movement – Because of user movement location data must be refreshed on a reasonable time interval.
  3. Varying accuracy – Location estimations coming from different providers must not be with the same accuracy. Location from newest provider might be significantly less accurate than the estimation coming from an older provider.

Having these three points in mind, and carefully choosing and optimizing your choices can make a huge difference in a good or poor user experience when it comes to GPS tracking.

NOTE: Android 4.4.2 (API 19) is used.

Listening for location updates

Let us start with a simple example where we retrieve LocationManager system service and assign LocationListener to it in order to handle location updates. LocationManager service allows applications to receive periodic updates of mobile device geographical location. LocationManager class is not initiated directly, it’s retrieved from system services. Let’s take a brief look on the source code here:

// Don't initialize location manager, retrieve it from system services.
LocationManager locationManager = (LocationManager) this
        .getSystemService(Context.LOCATION_SERVICE);
 
LocationListener locationListener = new LocationListener() {
 
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
 
    @Override
    public void onProviderEnabled(String provider) {
        Toast.makeText(MainActivity.this,
                "Provider enabled: " + provider, Toast.LENGTH_SHORT)
                .show();
    }
 
    @Override
    public void onProviderDisabled(String provider) {
        Toast.makeText(MainActivity.this,
                "Provider disabled: " + provider, Toast.LENGTH_SHORT)
                .show();
    }
 
    @Override
    public void onLocationChanged(Location location) {
        // Do work with new location. Implementation of this method will be covered later.
        doWorkWithNewLocation(location);
    }
};
 
long minTime = 5 * 1000; // Minimum time interval for update in seconds, i.e. 5 seconds.
long minDistance = 10; // Minimum distance change for update in meters, i.e. 10 meters.
 
// Assign LocationListener to LocationManager in order to receive location updates.
// Acquiring provider that is used for location updates will also be covered later.
// Instead of LocationListener, PendingIntent can be assigned, also instead of 
// provider name, criteria can be used, but we won't use those approaches now.
locationManager.requestLocationUpdates(getProviderName(), minTime,
        minDistance, locationListener);

HINT: When you run this code, you will see “Provider enabled” and “Provider disabled” messages as you receive location updates from providers.

You may have noticed that we assigned a minimum time (minTime) and minimum distance (minDistance) to requestLocationUpdates along side with LocationListener to LocationManager?

This might not seem that much important to you, but a careful choice of these values can make a difference.

  1. minTime – Wise choice of value for minTime will help you reduce power consumption. What you need to keep in mind is that elapsed time between location updates will never be less than minTime, but it can be greater, because it is influenced by the implementation of each given Provider and the update interval requested by other running applications.
  2. minDistance – Unlike minTime this parameter can be turned off by setting it’s value to 0. However, if minDistance is set to a value greater than 0, location provider will only send updates to your application if location has changed at least by given distance. This parameter is not a great power saver like minTime, but it should be kept in mind although.

These two parameters work in AND relation, so in order to receive location update, both of their conditions need to be achieved (i.e. more then 5 seconds has passed, and the distance change is greater than 10 meters).

Provider name that should also be provided for requestLocationUpdates will be covered later when we go trough the part of making our best provider pick criteria.

Implementation of LocationListener allows us to have some information about the state of used provider. We can monitor provider status (onStatusChanged) which gives us information if provider is unable to retrieve a location, or has became available after a period of inactivity, also we can monitor when given provider is disabled (onProviderDisabled) or enabled (onProviderEnabled). Most important method of LocationListener to us is the onLocationChanged method where Location is retrieved, we will go trough it later when describing location strategies, let’s go trough basics first.

What is a Location?

location

Location is data class representing geographical location. It has many properties that might come in handy, but let’s focus on the most important ones:

  • Provider – Name of the provider that generated the location fix. This can be used to assess the accuracy and confidence of location.
  • Accuracy – Estimated accuracy of given location in meters. It is defined as accuracy with 68% confidence. This means that if you draw a circle at the center of latitude and longitude of given location, the circle represented by this parameter has the probability of 68% that it contains the exact location of the device. Keep in mind that accuracy level depends very much from the nature of used provider and it can vary significantly.
  • Time – Represents the UTC time in milliseconds when the given location fix has been generated. Assuming that older locations have a much greater probability to be false than new ones can lead to “developer traps” because location fix from 10 seconds ago can still have a better accuracy than newer location coming from a much more accurate provider.
  • LatitudeLongitude and Altitude are parameters defining received geographical location.

Choose your provider

To begin making your location aware application, it’s best to start of by choosing the desired provider that suits your needs best. Android API made a nice way to help you overcome your dilemma.They gave us the Criteria which is a neat class for building up the criteria by which location provider is chosen. You must think of criteria as a way not to filter out existing providers, but as a list order criteria for location providers. Providers can be ordered according to accuracy, power usage, ability to report altitude, speed, and bearing, and monetary cost. Yes, in most cases all you need is a good choice, so please take a look at source code below:

/**
 * Get provider name.
 * @return Name of best suiting provider.
 * */
String getProviderName() {
    LocationManager locationManager = (LocationManager) this
            .getSystemService(Context.LOCATION_SERVICE);
 
    Criteria criteria = new Criteria();
    criteria.setPowerRequirement(Criteria.POWER_LOW); // Chose your desired power consumption level.
    criteria.setAccuracy(Criteria.ACCURACY_FINE); // Choose your accuracy requirement.
    criteria.setSpeedRequired(true); // Chose if speed for first location fix is required.
    criteria.setAltitudeRequired(false); // Choose if you use altitude.
    criteria.setBearingRequired(false); // Choose if you use bearing.
    criteria.setCostAllowed(false); // Choose if this provider can waste money :-)
 
    // Provide your criteria and flag enabledOnly that tells
    // LocationManager only to return active providers.
    return locationManager.getBestProvider(criteria, true);
}

There is a nice article here taking the providers story more in detail, but to make a long story short, here are the available providers with pros and cons for each one.
providers

User permissions

For your application to access location services, you must teach it to request permissions that user will need to confirm upon application install.

Thankfully location services don’t require many permissions that could spook an user into paranoia, there are only two permission groups of interest:

  1. ACCESS_COARSE_LOCATION – includes permission only for NETWORK_PROVIDER.
  2. ACCESS_FINE_LOCATION – includes permission both for NETWORK_PROVIDER and GPS_PROVIDER.
<manifest ... >
    <!-- Use one of these permissions that suit your needs most. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

Location strategies

For best user experience and lowest power consumption good location strategy should be devised. Meaning of the term location strategy is basically the way you decide to implement when location acquisition is done, what provider you use and how long do you intend to keep on using it. There are a couple of rules you might want to follow:

  • When acquiring initial location fix, use fastest provider. Network or passive provider give fastest location fix, although they are also most unreliable. Use these providers to give initial hint to user about their location and later improve accuracy as more accurate providers initialize. There is a way to make a fast fix easily:
String provider = LocationManager.NETWORK_PROVIDER;
 
// Returns last known location, this is the fastest way to get a location fix.
Location fastLocation = locationManager.getLastKnownLocation(provider);
  • While waiting for GPS provider to initialize (or another more accurate one), handle location fixes from other providers, filter them by dismissing less accurate and older ones. Hold on to most accurate location, don’t throw it away. A sample algorithm of filtering out locations is below.
/**
* Make use of location after deciding if it is better than previous one.
*
* @param location Newly acquired location.
*/
void doWorkWithNewLocation(Location location) {
    if(isBetterLocation(getOldLocation(), location) {
        // If location is better, do some user preview.
        Toast.makeText(MainActivity.this,
                        "Better location found: " + provider, Toast.LENGTH_SHORT)
                        .show();
    }
 
    setOldLocation(location);
}
 
/**
* Time difference threshold set for one minute.
*/
static final int TIME_DIFFERENCE_THRESHOLD = 1 * 60 * 1000;
 
/**
* Decide if new location is better than older by following some basic criteria.
* This algorithm can be as simple or complicated as your needs dictate it.
* Try experimenting and get your best location strategy algorithm.
* 
* @param oldLocation Old location used for comparison.
* @param newLocation Newly acquired location compared to old one.
* @return If new location is more accurate and suits your criteria more than the old one.
*/
boolean isBetterLocation(Location oldLocation, Location newLocation) {
    // If there is no old location, of course the new location is better.
    if(oldLocation == null) {
        return true;
    }
 
    // Check if new location is newer in time.
    boolean isNewer = newLocation.getTime() > oldLocation.getTime();
 
    // Check if new location more accurate. Accuracy is radius in meters, so less is better.
    boolean isMoreAccurate = newLocation.getAccuracy() < oldLocation.getAccuracy();       
    if(isMoreAccurate && isNewer) {         
        // More accurate and newer is always better.         
        return true;     
    } else if(isMoreAccurate && !isNewer) {         
        // More accurate but not newer can lead to bad fix because of user movement.         
        // Let us set a threshold for the maximum tolerance of time difference.         
        long timeDifference = newLocation.getTime() - oldLocation.getTime(); 
 
        // If time difference is not greater then allowed threshold we accept it.         
        if(timeDifference > -TIME_DIFFERENCE_THRESHOLD) {
            return true;
        }
    }
 
    return false;
}

You can find many samples of location filtering online, this one is much similar to the one provided by Android documentation here.

  • After handling a location fix with suitable accuracy, display it to user and stop listening to location updates (helps conserve power).
LocationManager locationManager = (LocationManager) this
                .getSystemService(Context.LOCATION_SERVICE);
 
// Stop listening to location updates, also stops providers.
locationManager.removeUpdates(locationListener);

Walking the rope

As your experience as an “Android Location Strategies” know-how guy expands over time, you will probably return to location filtering algorithm periodically to tweak it in order to achieve better results. For the time being, understanding the answers to questions like “How does an normal location acquisition timeline look like?” and “How can a newer location be less accurate than an old one?” should make your life significantly easier.

If you decide to follow guidelines, your typical timeline should look something like this:

  • Start listening for locations, get a low accuracy level quick fix. Keep in mind that quick fix location will be received from network provider (if you are not sure, check the name of provider on given Location). Preserve this location until a better one arrives.
  • If new location fix is received, do a comparison against it’s provider, time and accuracy and dismiss it only in case that the new one is better. If this the first update you received after quick fix, accept only locations coming from WI-FI or GPS.
  • You will need to wait some time until GPS provider has warmed up (found sufficient number of satellites for precise location estimate). During this time, you will receive numerous location fixes from network and WI-FI provider. Preserve best location by your filtering algorithm.
  • To make things even harder, if user is on move, acquiring precise location can be a very challenging task. Best approach is to use faster (smaller) location update intervals until GPS provider becomes available. Larger quantity of locations in a time interval will allow you to make the best pick, but remember, put more precise providers ahead in decision queue.
  • After receiving location with sufficiently high accuracy, depending of your application, you can decide to stop listening to locations if you need a rough estimate of users bearing or slow down the location update intervals in case you need to track users movement trough time.

getting-location

Answer to question “How can a newer location be less accurate than an old one?” comes directly from the timeline: Because of user movement and varying accuracy of received locations, a single parameter, in this case “time” can’t be used to precisely make a judgement of users whereabouts. When designing location filtering algorithm always use at least these three parameters: Provider (name of provider), Accuracy and Time.

Kommentare

  • Chuck

    Your code looks interesting but I’m new to this and I’m getting stacks of errors, syntax errors and the like.

    Where do I put the code? Looks like MainActivity.java so I put it there and got lots of errors.

    Initially tried it with the first block of code, then added the others to it, which made it worse.

    A complete ready-to-use example would be nice.

    Any advice?

    Thank you.

    • aron.sreder

      18. August 2014 von aron.sreder

      Hello Chuck, thank you for your interest in my blog post. Our goal here at Codecentric is to help out both newbies and more experienced developers, so don’t worry 😉

      Regarding syntax errors, if you are using Eclipse as your desired IDE, please be sure that when you are creating new project you chose new “Android Project”, and for offered API levels you pick “Android 4.4.2 (API 19)”, this will make sure you have all of the necessary dependencies included in the project.

      The given code samples from above you can use in any activity you wish (location services are going to be active as long as you keep that “view” opened in your phone).

      It is a little bit complicated for me to explain step by step guide for code design in this reply, so I will try to make a ready-to-use sample till the end of the week.

      It is not possible to just copy & paste the given code samples from above into your MainActivity class, you need to place the initialization of LocationManager and LocationListener either in activity onCreate() or onResume() methods.

      Check out my post again in a couple of days, and hopefully I will manage my schedule and come up with a simple example of code.

  • Chuck

    Thanks for the advice, Aron! Very helpful.

    A complete example of the first block of code would be great to get started.

    This is a very good article and is the best I have seen on the subject.

    • aron.sreder

      20. August 2014 von aron.sreder

      Hello, I have put together an MainActivityExample where you can see how activity looks like with given code. Click Here

      • Farooq Araali

        9. October 2014 von Farooq Araali

        why do i keep getting invalid link whenever i try to access the link you put up.

  • Chuck

    Sorry, Aron. Getting “system services not available before oncreate” with your example.

    • aron.sreder

      21. August 2014 von aron.sreder

      Sorry, just modify this.
      Leave:

      private LocationListener locationListener;

      and place

      locationListener = new LocationListener() {
      …. contents …
      }

      into onCreate().

  • Punit

    onLocatioChanged is not called . i set minTime to 2 seconds, distance to 0. But onLocatioChanged is not calld. Would u pls help?

  • Ronandroid

    Hello, thk for your code WELL detailled !!

    i have one question (can you confirm if i understand it well)

    getProviderName return ONE provider the most “accurate” to your parameters
    But how can i work with “both” provider GPS and NETWORK ?
    I mean how can I , at first try, use the network, then switch to the GPS ?

    in the on create i thought to put this :

    locationManager1.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, minTime,minDistance, locationListener);
    //first try to reach network
    locationManager2.requestLocationUpdates(getProviderName(), minTime,minDistance, locationListener);
    //then open a new provider, most accurate … like GPS

    and once locationManager2is active kill locationManager1
    or did i made a mistake somewhere (of course, with the good declaration at the beginning of code 🙂 )

  • Kostas

    Thank you for sharing, i have started with a new app and i wanted a nice tutorial on how to use gps tracking.

  • Kayalvizhi

    Hi all,
    Thanks for the useful information.am in the beginner of programming in android apps.

    i want to update the location of vehicle to the customers android mobile app…
    is this possible? if yes ,how to do?

    i have idea how to get vehicle latitude and longitude data using GPRS/GSM/GPS wireless modules. but i don’t know how to display as location(i.e city name /street name)in adroid mobile .am interested this android apps development.
    anyone can help me to complete this one?

  • aron.sreder

    12. September 2014 von aron.sreder

    Hello Kayalvizhi,

    please take a look at this.

    You can retrieve list of “nearest” addresses by latitude and longitude using getFromLocation method, documentation is here.

  • Tauhid

    Hi ARON.SREDER,
    Your code is very much helpful for me. a quick question is can you tell me that is it possible to set a min accuracy and take get the lat lon? like when the accuracy is 5 meters then take the gps point.

    Thanks in Advance.
    Tauhid

  • Kelvin Wang

    24. October 2014 von Kelvin Wang

    Very nice article!!! So helpful!!

  • Sahn

    Hi Aron,

    Very good article. I have a question regarding accuracy, I am trying to measure distance travelled from one point to another point. I want to display some marker every mile. Currently, my distance is less than what ever I travelled.

    Can you please suggest any accuracy parameters?

    Thanks,
    Sahn

  • Peter

    Hallo,

    where are the getOldLocation and setOldLocation-Methods? Is there anywhere a complete code of this example?

    thanks
    Peter

  • majid

    Hi.
    Please help.
    I want to remove permissionACCESS_FINE_LOCATION from manifest,but when I remove it from the manifest, then run the program is Fc, please help, how do I remove this permission?

    Sorry if my English is not good…

  • sushmitha

    17. February 2015 von sushmitha

    GPSTracker gps = new GPSTracker(AppActivity.this);
    am getting error in gpstracker wat to do?

  • John Martin

    27. March 2015 von John Martin

    Hello…this is good stuff. We are trying to build this with ionic instead of core as we are building a cross platform app…does this apply only to native installations or cross platform as well

  • Firemaps

    28. March 2015 von Firemaps

    Hi all,

    I have plenty of GIS experience but not so much with Android applications.

    I am hoping to use my Samsung Galaxy S3 to collect point data in the field however my geotagged photos keep turning up the same location.

    Can anyone tell me how to go about this in simple English please? I’m sure many others would appreciate it too!

    Thanks

  • AndroidDeveloper

    8. April 2015 von AndroidDeveloper

    Very Clear Explanation.Helpful for newbie in android like me..
    i want to ask if i need location update ONLY when request by user in this case what will be the
    MINIMUM TIME value & MINIMUM DISTANCE?
    Thank u!

  • Rjesh

    22. April 2015 von Rjesh

    sample code link not working ……..

Comment

Your email address will not be published. Required fields are marked *