I want to get 5 consecutive update locations in order to check if the user is moving and to make sure the GPS is calibrated. I did the following:
I added android.permission.ACCESS_FINE_LOCATION" to the manifest and in onConnected:
mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(1000); // Update location every second
mLocationRequest.setFastestInterval(1000);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
and in onLocationChanged I did the following:
locationRetries++;
switch (locationRetries) {
case 1: {
firstLatitude = location.getLatitude();
firstLongitude = location.getLongitude();
}
case 5: {
locationRetries = 0;
lastLatitude = location.getLatitude();
lastLongitude = location.getLongitude();
accuracy = Math.round(location.getAccuracy());
stopLocationUpdates();//will remove from location updates and disconnects
float[] results = new float[1];
Location.distanceBetween(firstLatitude, firstLongitude, lastLatitude, lastLongitude, results);
if (results[0] > 5)... //if moved at least 5meters, show popup that the user is moving else do your thing
}
Now I have 3 issues:
1) It seems to take much less than 5 seconds to retrieve the 5 updates even though I set both parameters to be 1000 milliseconds.
2) All the 5 locations (at least the first and last ones) are the same exact location even though I was walking fast. I thought I maybe moving too slow so
3) I closed my app, reopened it on a far location and pressed the button. Almost instantly I got the previous location. I pressed the button again and then I got the real location I was on. It's as if it didn't really asked/waited for a location from the GPS but instead took the last one which was remotely inaccurate at the time. I don't have any "get last known location" code.
I guess the bottom line would be: how can I make sure that it really asks the GPS where am I when I asked for the location and also when asking it for 5 consecutive times, to give me the real locations and not from the cache(?).
The Fused Location Provider
intelligently manages the underlying location technology and gives you the best location according to your needs.
Simple APIs: Lets you specify high-level needs like "high accuracy" or "low power", instead of having to worry about location providers.
Immediately available: Gives your apps immediate access to the best, most recent location.
Power-efficiency: Minimizes your app's use of power. Based on all incoming location requests and available sensors, fused location provider chooses the most efficient way to meet those needs.
Versatility: Meets a wide range of needs, from foreground uses that need highly accurate location to background uses that need periodic location updates with negligible power impact.
There is no guarantee that the fused location provider will ever use GPS - if it has a recent location of the accuracy you request it will return until a better location is returned (i.e., live GPS is returning accurate locations). This ensures that you'll get a better location sooner without waiting for GPS to be primed.
If you specifically need data from GPS, you need to use the LocationManager using the GPS_PROVIDER.
If you are trying to determine what the user is currently doing, you can instead use the ActivityRecognitionApi, which returns DetectedActivity such as WALKING or STILL: using that would give a faster method to understand what the user is currently doing.
Related
I'm starting to develop an app that will stay in background forever and detect when a user is staying in a certain location for a while (and then display a notification to invite the user to open the app to obtain informations about that place).
It's something very similar to what Google Maps does when you're in a restaurant and it shows you a notification to check ratings about it.
What I want is to have a minimal impact on device, so location updates should be very "passive", getting the location only when user is not moving and, if possible, recycling location data that is already got by other activities - maybe by Google Maps itself or other location apps that are running on the devices.
This is not a navigation app, so I don't need to have the live fine location but simply the approximate place with the best accuracy and minimal effort, and only if user is not moving.
LocationListener and onLocationChanged seems to be my men, but can I specify that I don't want to trigger device's sensors and only re-use location data when it's available for other scopes? My app should check these informations and then decide to do a reverse geocode if and when they are accurate enough.
Yes, LocationListener and onLocationChanged are your men, though for a passive implementation, there are a few options you can go through.
First you can check for the last known location, maybe compare it in terms of its time; i.e. getTime() and verify whether it is of use to you.
In terms of code, literally...
Google samples, android location has what is relevant for the last location part:
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
// Provides a simple way of getting a device's location and is well suited for
// applications that do not require a fine-grained location and that do not need location
// updates. Gets the best and most recent location currently available, which may be null
// in rare cases when a location is not available.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Further, you can combine it with LocationRequest object, this helps your implementation as you can call it right after trying getLastLocation() and basically have a more reliable arrangement for obtaining location.
// Create the location request
mLocationRequest = LocationRequest.create()
//priority object needs to be set, the following will definitely get results for you
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY)
//interval updates can be on the lines of 15mins or 30... acc to your requirement
.setInterval(UPDATE_INTERVAL)
.setFastestInterval(FASTEST_INTERVAL);
// Request location updates
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
i suggest give PRIORITY_NO_POWER a try, could work well in combination with getLastLocation(). These power modes have been added specifically for optimising battery/power consumption and efficiency for retrieving location.
I have some code that runs multiple times per second in my app. I'm trying to get my location in every cycle. I am using the following:
Location myLastPos = LocationServices.FusedLocationApi.getLastLocation(googleApiClient)
My app also runs in the background using a PARTIAL_WAKE_LOCK. With the screen on everything seems OK. When I turn the screen off my app still runs normally but I no longer get location updates consistently.
It appears that I get updates much less frequently (often minutes in between updates). I'm checking the timestamp of the location using:
myLastPos.getElapsedRealtimeNanos()
I also found that even when the screen is on I get some strange results. Sometimes I get a few milliseconds between updates, other times I get a few seconds. This is all very concerning. Can someone either help me use FusedLocationApi properly or suggest an alternative. All I really want is to poll the gps directly for lat/long a few times a second without google libraries getting in the way.
The getLastLocation() method just gets the last known location that the device happens to know. The "last known location" here means exactly that: It may not be up-to-date. Locations do come with a time stamp which could be used to asses if the location might still be relevant.
The device doesn't determine its location on its own, but only when some application request the location. So your app is now dependent on other applications requesting location updates.
If you need updates every few seconds, then request regular location updates yourself.
Android documentation recommends the FusedLocationProvider, but the LocationManager is also a perfectly valid option, if there's any reason to avoid the Google Play services.
The basic idea is to first request location updates:
// Using LocationManager as an example.
mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Using GPS, requesting location updates as soon as available and even for
// the smallest changes. Here 'this' refers to our LocationListener
// implementation.
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
The updates are then received by a listener:
#Override
public void onLocationChanged(Location location) {
// We received a location update.
// Copy the value from the method parameter to our
// class member variable.
mLocation = location;
}
And when you no longer need the updates you should cancel the request:
mLocationManager.removeUpdates(this);
The approach is very similar for the FusedLocationProvider.
I have used onLocationChanged method of LocationListener for detecting change in location of my device. In requestLocationUpdates method I have set minimum Time = 5 seconds and minimum Distance = 2 meters, but requestLocationUpdates method is giving me updates even when my device is not moved at all (placed stationary). So please tell what is the issue with my code?
This my code:
public class LocationDetector implements LocationListener {
#Override
public void onLocationChanged(Location location) {
Log.d("GPS: ",location.getLatitude()+", "+location.getLongitude());
}
.
LocationManager manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
.
manager.requestLocationUpdates("gps", 5000, 2, new LocationDetector());
The GPS location can fluctuate. If you actually log the locations that you receive, you'll probably see that the location actually changes by 2 meters or more whenever a location update comes.
Edit: Ideas for dealing with the fluctuation added:
How to deal with GPS fluctuation depends on your application's needs. Some possible ideas include:
If you don't need a really accurate location, then use a higher distance limit in the requestLocationUpdates() call to not receive updates for very small location changes. You can think what's the absolutely necessary accuracy required by your use case and then use the highest possible distance limit.
If you don't expect the location to change very quickly or you don't need to react to location changes very quickly, then use a higher time limit in the requestLocationUpdates() call. This also makes sense if you have some very heavy code triggered by onLocationChanged() like if you always fetch some data over the network (reverse geocoding etc.).
The time limit also has more impact on the battery usage. Android documentation says:
...it is more difficult for location providers to save power using the
minDistance parameter, so minTime should be the primary tool to
conserving battery life.
If you really need an accurate location then there are some ways to decrease the fluctuation.
First of all the Location object received in onLocationChanged() usually has an estimated accuracy available by calling the Location.getAccuracy() method. You can simply ignore any location updates that have very poor accuracy (compared to the accuracies of previous location updates).
You can also do some filtering if you have a short buffer of the most recent locations. Calculating an average will reduce the amount of sudden changes but it also increases the response time. That is: a rapid change in the location will completely show up in the averaged location data only after some time. (Of course the averaged data starts to move towards the location right away, but it takes a while.) Also it will "let the spikes trough" to some amount.
If a fast response time is important and any major "spikes" in the data should be eliminated, then calculating the median is a better option. It will not smooth out the small changes in the data that much, but random spikes can be filtered out. If there's a real (permanent) sudden change in location, then the median filtering reacts to that with only a very small delay.
(These things are easy to try out in your favourite spreadsheet application.)
I know with the Android and the Location API's it's possible to receive periodic updates of latitude and longitude through an interval of time using the "setInterval" method and other methods on the "LocationRequest" class, but what I'm wondering is, what would be a good way to get latitude and longitude updates via GPS based off of a change in distance compared to the previous location without having it on all the time and not wasting battery (preferably a solution that is energy efficient).
For example, say I start at one place and then 2 minutes later I'm 5km from the distance that I originally started at. At this point the GPS would detect the change in location and update the latitude and longitude on the device.
If anyone could provide some insight or code samples on how to do this, that'd be great!
Thanks
You can based your position attribute update on how far you are from the previous location, but you can't based GPS frequency update on that, simply because you need a location request to find out that you are far enough.
If you want an energy efficient solution, you can reduce the rate at which new update appear. You can also reduce the window duration in which you listen for position (position may be less accurate).
A lot of information can be found here, specially in Adjusting the model to save battery and data exchange and Deciding when to start listening for updates chapters
You can get the location change if you specify the distance you want to get updates in the requestLocationUpdate parameter.
public void requestLocationUpdates (String provider, long minTime, float minDistance, LocationListener listener)
Here is example:
LocationManager yourLoc = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
yourLoc.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 2, onLocationChange);
Here you will receive location changes after every 2 meters you have travelled, irrespective of the time you spent at a particular location.
Hope that answers your question!!!
I am receiving periodically location updates via GPS, the problem is the time until the first location update occurs - it is just to long. When the user is within a building, this time gets even worser.
Therefore I am looking for some strategies to improve the startup time (at the moment I don't use getLastKnownLocation but I will). I started reading A Deep Dive Into Location to get some ideas and have some thoughts now I would like to discuss with you.
At first the blog post mentioned above goes through each location provider on the device and queries for the getLastKnownLocation, but only considers them when they are not to old and within an allowed accuracy.
Here comes my first question: I would expect to have the network based location immediately available, so I would request a new network based location, then I would get getLastKnownLocation from the GPS-provider and if the last GPS-location is within the circle formed by the point network based location and radius accuracy then I would take the GPS location, regardless how old it is. What do you think?
After checking the last known location I will start the GPS position tracking and because I only need it every 2 seconds and 10m change, I would use
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2 * 1000, 10, this);
However I found a pattern (inside the presentation Being Epic: Best Practices for Android Development on slide 95 and 96 that at first he turns on coarse and fine locations (with 0,0 for time and distance changes) and then, when the first event is received, he switches to the really needed update frequency. So, does the first gps-update comes faster when the interval is set to 0?
In opposite to this pattern I would keep the coarse update until the first GPS update is received. What do you think?
Hope to get some interesting answers!
----------------Update----------------
I did some investigations: I turned on GPS and waited to get a fix. Then I turned GPS off and drove 50km (31 miles). Then I used the code from A Deep Dive Into Location to get all the getLastKnownLocation. I tried it twice, first with GPS turned off and second with GPS turned on, but without a fix:
1) with GPS turned off I got
- Provider: network, correct location with accuracy 680m
- Provider: passive (mProvider=network), same location as above, same time as above
- Provider: gps, location null
So I learned that when gps is turned off you get no getLastKnownLocation.
2) with GPS turned on I got
- Provider: network, correct location with accuracy 652m
- Provider: passive (mProvider=network), same location as above, same time as above
- Provider: gps, location as it was 2h earlier with accuracy 12m, time was also 2h earlier
Here I learned that old messages are not invalidated, even it is obvious that they are wrong.
In that case the algorithm works fine, because I moved in the time I turned the GPS off.
But what if I enter a house? In that case the 2h old GPS result would be very good, even it is outdated.
At the Google IO 2013 they showed in the session Beyond the Blue Dot: New Features in Android Location a new approach, see the video here.
The engineers at Google tried a lot of different strategies, the result is the "Fused location provider". Its quality is shown at 12:17 in the video.
Ketan Parmar has published a blog post how the fused location provider can be used in an example application.
Start up the location manager with the passive provider first
locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, AlarmManager.INTERVAL_FIFTEEN_MINUTES, 75, this);
then once you get a response from that you can switch to your gps hardware.
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);