Beliebte Suchanfragen

Cloud Native

DevOps

IT-Security

Agile Methoden

Java

//

Android: GPS positioning and location strategies

14.5.2014 | 11 minutes of reading time

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:

1// Don't initialize location manager, retrieve it from system services.
2LocationManager locationManager = (LocationManager) this
3        .getSystemService(Context.LOCATION_SERVICE);
4 
5LocationListener locationListener = new LocationListener() {
6 
7    @Override
8    public void onStatusChanged(String provider, int status, Bundle extras) {
9    }
10 
11    @Override
12    public void onProviderEnabled(String provider) {
13        Toast.makeText(MainActivity.this,
14                "Provider enabled: " + provider, Toast.LENGTH_SHORT)
15                .show();
16    }
17 
18    @Override
19    public void onProviderDisabled(String provider) {
20        Toast.makeText(MainActivity.this,
21                "Provider disabled: " + provider, Toast.LENGTH_SHORT)
22                .show();
23    }
24 
25    @Override
26    public void onLocationChanged(Location location) {
27        // Do work with new location. Implementation of this method will be covered later.
28        doWorkWithNewLocation(location);
29    }
30};
31 
32long minTime = 5 * 1000; // Minimum time interval for update in seconds, i.e. 5 seconds.
33long minDistance = 10; // Minimum distance change for update in meters, i.e. 10 meters.
34 
35// Assign LocationListener to LocationManager in order to receive location updates.
36// Acquiring provider that is used for location updates will also be covered later.
37// Instead of LocationListener, PendingIntent can be assigned, also instead of 
38// provider name, criteria can be used, but we won't use those approaches now.
39locationManager.requestLocationUpdates(getProviderName(), minTime,
40        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  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:

1/**
2 * Get provider name.
3 * @return Name of best suiting provider.
4 * */
5String getProviderName() {
6    LocationManager locationManager = (LocationManager) this
7            .getSystemService(Context.LOCATION_SERVICE);
8 
9    Criteria criteria = new Criteria();
10    criteria.setPowerRequirement(Criteria.POWER_LOW); // Chose your desired power consumption level.
11    criteria.setAccuracy(Criteria.ACCURACY_FINE); // Choose your accuracy requirement.
12    criteria.setSpeedRequired(true); // Chose if speed for first location fix is required.
13    criteria.setAltitudeRequired(false); // Choose if you use altitude.
14    criteria.setBearingRequired(false); // Choose if you use bearing.
15    criteria.setCostAllowed(false); // Choose if this provider can waste money :-)
16 
17    // Provide your criteria and flag enabledOnly that tells
18    // LocationManager only to return active providers.
19    return locationManager.getBestProvider(criteria, true);
20}

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.

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.
1<manifest ... >
2    <!-- Use one of these permissions that suit your needs most. -->
3    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
4    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
5</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:
1String provider = LocationManager.NETWORK_PROVIDER;
2 
3// Returns last known location, this is the fastest way to get a location fix.
4Location 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.
1/**
2* Make use of location after deciding if it is better than previous one.
3*
4* @param location Newly acquired location.
5*/
6void doWorkWithNewLocation(Location location) {
7    if(isBetterLocation(getOldLocation(), location) {
8        // If location is better, do some user preview.
9        Toast.makeText(MainActivity.this,
10                        "Better location found: " + provider, Toast.LENGTH_SHORT)
11                        .show();
12    }
13 
14    setOldLocation(location);
15}
16 
17/**
18* Time difference threshold set for one minute.
19*/
20static final int TIME_DIFFERENCE_THRESHOLD = 1 * 60 * 1000;
21 
22/**
23* Decide if new location is better than older by following some basic criteria.
24* This algorithm can be as simple or complicated as your needs dictate it.
25* Try experimenting and get your best location strategy algorithm.
26* 
27* @param oldLocation Old location used for comparison.
28* @param newLocation Newly acquired location compared to old one.
29* @return If new location is more accurate and suits your criteria more than the old one.
30*/
31boolean isBetterLocation(Location oldLocation, Location newLocation) {
32    // If there is no old location, of course the new location is better.
33    if(oldLocation == null) {
34        return true;
35    }
36 
37    // Check if new location is newer in time.
38    boolean isNewer = newLocation.getTime() > oldLocation.getTime();
39 
40    // Check if new location more accurate. Accuracy is radius in meters, so less is better.
41    boolean isMoreAccurate = newLocation.getAccuracy() < oldLocation.getAccuracy();       
42    if(isMoreAccurate && isNewer) {         
43        // More accurate and newer is always better.         
44        return true;     
45    } else if(isMoreAccurate && !isNewer) {         
46        // More accurate but not newer can lead to bad fix because of user movement.         
47        // Let us set a threshold for the maximum tolerance of time difference.         
48        long timeDifference = newLocation.getTime() - oldLocation.getTime(); 
49 
50        // If time difference is not greater then allowed threshold we accept it.         
51        if(timeDifference > -TIME_DIFFERENCE_THRESHOLD) {
52            return true;
53        }
54    }
55 
56    return false;
57}

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).
1LocationManager locationManager = (LocationManager) this
2                .getSystemService(Context.LOCATION_SERVICE);
3 
4// Stop listening to location updates, also stops providers.
5locationManager.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.

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.

share post

Likes

0

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.