views:

273

answers:

2

My app checks at a specific time whether a user is at a given location. I use the alarm manager to start a service that makes this call:

locationManager.requestLocationUpdates(bestProvider, 0, 0, listener);

And also checks:

 locationManager.getLastKnownLocation(bestProvider);

But I'm having problems when running on a real device. For one thing, getLastKnownLocation is most likely the last place the GPS was on, which could be anywhere (i.e., it could be miles from the user's current location). So I'll just wait for requestLocationUpdates callbacks, and if they aren't there within two minutes, remove the listener and give up, right?

Wrong, because if the user's location is already stable (i.e., they've used GPS recently and haven't moved) then my listener will never be called because the location doesn't change. But the GPS will run until my listener is removed, draining the battery...

What is the right way to get the current location without mistaking an old location for the current location? I don't mind waiting a few minutes.

EDIT: It's possible that I'm wrong about the listener not being called, it may just take a little longer than I thought... Hard to say. I'd appreciate a definitive answer still.

+4  A: 

If the user's location is already stable, then getLastKnownLocation will return the current location. I'd call getLastKnownLocation first, look at the timestamp (Location.getTime()) then register a listener if the fix is too old.

cristis
Thanks, I didn't know about getTime().The problem with this is, how old does a time have to be before the location provider considers it too old? How can I guarantee that my listener will be called if I don't use that location?
noah
The location provider doesn't consider it's too old, it never discards it (as far as I know). You have to decide when YOU consider the fix is too old and request updates if necessary. Then you can still use the old location if there is no update within some given time.
cristis
+3  A: 

The code may be something like that:

public class MyLocation {
    Timer timer1;
    LocationManager lm;

    public boolean getLocation(Context context)
    {
        lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
        timer1=new Timer();
        timer1.schedule(new GetLastLocation(), 20000);
        return true;
    }

    LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location) {
            timer1.cancel();
            lm.removeUpdates(this);
            //use location as it is the latest value
        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

    class GetLastLocation extends TimerTask {
        @Override
        public void run() {
             lm.removeUpdates(locationListenerGps);
             Location location=lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
             //use location as we have not received the new value from listener
        }
    }
}

We start the listener and wait for update for some time (20 seconds in my example). If we receive update during this time we use it. If we don't receive an update during this time we use getLastKnownLocation value and stop the listener.

You can see my complete code here http://stackoverflow.com/questions/3145089/what-is-the-simplest-and-most-robust-way-to-get-the-users-current-location-in-an/3145655#3145655

EDIT (by asker): This is most of the answer, but my final solution uses a Handler instead of a Timer.

Fedor
Can you provide some example code? e.g., how would you wait 2 minutes or something like that if the location listener isn't called?
noah
Added code snippet
Fedor