I am trying the 'LocationUpdates' sample from http://developer.android.com/training/location/receive-location-updates.html . This application gets and prints location notifications.
I am trying to change the interval of the location updates according to my latest location.
So - I had added mLocationRequest.setInterval() into onLocationChanged
The result is very wrong. My application is bombarded with many location updates (few a second!!!!)
My only change to the sample is this:
private int x=0;
#Override
public void onLocationChanged(Location location) {
// Report to the UI that the location was updated
mConnectionStatus.setText(R.string.location_updated);
// In the UI, set the latitude and longitude to the value received
mLatLng.setText(String.valueOf(x++));
mLocationRequest.setInterval(1000); // Change 1
mLocationClient.requestLocationUpdates(mLocationRequest, this); // Change 2
}
How can I change the interval inside onLocationChanged ?
I think that the problem is that requestLocationUpdates resets the last request, and then immediately sends another notification. so a loop is created. (faster than the fastest interval). so I need a reliable way to change the interval of a 'live' LocationRequest
You are not supposed to call mLocationClient.requestLocationUpdates(mLocationRequest, this); inside onLocationChanged(Location location)
since you are registering the listener again, and you will get the first call immediately.
so what i would do would be:
dont call mLocationClient.requestLocationUpdates(mLocationRequest, this); and see if anyways mLocationRequest.setInterval(1000); is taking effect
if this doesnt work, try to unregister the listener, and then use a trick to wait before registering it again with the new settings, something like:
Handler h = new Handler();
#Override
public void onLocationChanged(Location location) {
//... all your code
mLocationRequest.setInterval(1000);
mLocationClient.removeLocationUpdates(LocationListener listener)
h.postDelayed (new Runnable(){
public void run(){
mLocationClient.requestLocationUpdates(mLocationRequest, YOUROUTTERCLASS.this);
}
}, 1000);
}
So during one second there is not registered listener, so you wont get any updated, and after that, the listener is registerered with that interval.
Try using mLocationRequest.setFastestInterval(long millis)
As mentioned in developer.android.com :
This allows your application to passively acquire locations at a rate faster than it actively acquires locations, saving power. Unlike setInterval(long), this parameter is exact. Your application will never receive updates faster than this value.
Try to use :
mLocationRequest.requestLocationUpdates("gps", 1000, 0, this);
However I don't agree to do a requestLocationUpdates in onLocationChanged event; In my opinion should be setted outside onLocationChanged Event...
Related
Iam developing an app that do some field signal strengths calculations every second by using Handler and during the Handler period it records the coordinates and record the results & coordinates. It works fine except when real testing and when I increase the speed of the vehicle the recorded coordinates not every second while some times every 2-3-4 seconds which is not accepted by me.
The Code below:
final LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
publicvoid method_01(LocationManager locationManager){
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Location locationDataRate = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
double latitude = locationDataRate.getLatitude();
double longitude = locationDataRate.getLongitude();
x=y+1;
Save to file (latitude,longitude,x);
handler.postDelayed(this, 1000);
}
}, 1000);
}
I have tried also to change the time and min. distance to (1000,0)
First of all, if you want to receive an update every 1 second, you should not request more frequent updates. Therefore:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this);
Anyway, the best practice for handling location updates is not persistently looping getLastKnownLocation but implementing the callback of location updates. There is a bundle of examples here.
To your question, in the doc they state that interval between updates in never guaranteed:
The location update interval can be controlled using the minTime
parameter. The elapsed time between location updates will never be
less than minTime, although it can be more depending on the Location
Provider implementation and the update interval requested by other
applications.
I have been trying to find the location obtained by GPS_Provider and Network_Provider for every 5 minutes and at the same time stamp for the two values obtained at any specific time.
I tried using the following location strategy given
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 300000, 0, locationListener)
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,300000, 0, locationListener)
But here the Location Manager will call the onLocationChanged() method of the listener if the time since last location update is greater than the notificationInterval.
This brings me some time stamp difference between values generated by GPS_Provider and Network_Provider after every 5 minutes.Is there any way such that I can find the location that GPS_Provider and Network_Provider generate at a same time stamp.
Example:
Now: GPS_Provider(lat,long) at 09:35:12 , Network_Provider(lat,long) at 09:35:14
I need: GPS_Provider(lat,long) at 09:35:12 , Network_Provider(lat,long) at 09:35:12
In short, no.
The location api doesn't work in a synchronous way, meaning you have no guarantee when exactly you'll get the update. the time frames which you supply state the maximum interval between updates.
Having said that, you could start the updates with lower interval (such as 50ms) and get a bunch of updates, sort them by the second of the timestamp and get the ones which have the same value.
Update
You can use the getLastKnownLocation method to get the last location known by the provider, check the docs here.
I'm trying to find a way to turn off the GPS immidietly in case a good enough location was found, while still having a time limit to "give up".
I tried to do this with the following strategy:
start checking for locations, as soon as a location that has an accuracy lower than the maximum tolerated, pass it to the next function for processing and stop looking for updates.
Also, to save battery life, if such location could not be found in 30 seconds, stop looking for location updates without passing a value (basically give up, and hope to better luck next time).
To count the 30 seconds, I'm using a handler. But as soon as I write the line locationManager.removeUpdates(locationListener); in the handler, the locationListener in the parenteses in both lines (the one in the handler and the one in the listener) turns red and reports an error: The local variable locationListener may not have been initialized
Here is my code:
private void checkProximity() {
final LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
//start tracking location
final LocationListener locationListener = new LocationListener() {
...
#Override
public void onLocationChanged(Location location) {
//if new accuracy is better than the best estimate - update the best estimate
if(location.getAccuracy() < MAXIMUM_TOLERATED_ACCURACY) {
//forward location to scanProximity and end the location search
scanProximity(location);
locationManager.removeUpdates(locationListener); //FIRST LINE (see below)
}
}
};
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
500, 0, locationListener);
Handler h = new Handler();
int delay = 30 * SECOND;
Runnable removeListener = new Runnable() {
#Override
public void run() {
//if this code is reached - the maximum tolerated accuracy was not met in the period time
//extended to find a location
//TODO stop the location manager and return without forwarding a value
locationManager.removeUpdates(locationListener); //as soon as I write this line, the FIRST LINE and this line turns red.
}
};
h.postDelayed(removeListener, delay);
}
Is there anyway I can do this differently so I won't get an error?
I recommend you use Little Fluffy Location Library to work with GPS locations. Check out the examples codes and see which makes you more easy the solution to your problem , this is a beautiful way.
I am running a service in the background that reads GPS/Network location and needs to do the following:
run in background without interruptions on app restart and keep it alive as much as possible without being killed (This is solved with the help of Merlin's comment bellow)
on a new location received, call a web service and send the read location
have a repetitive task running every 60 seconds and resend last location to the web service. This will help in case the user stays in the same position.
There are a few things I have considered and I'm not sure if I understood right. The service runs in the same thread as the main app, so sending the location to the server on same thread as the UI thread may trigger UI freezes and this is not good. Also I'm not sure if GPS/Network listeners have their own threads or use the same thread as the app.
Here is a shortened code of the service to make things clearer:
public class GPSLoggerService extends Service {
#Override
public void onCreate() {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 50, locationListenerNetwork);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 50, locationListenerGps);
scheduleTaskExecutor = Executors.newScheduledThreadPool(5);
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
updateLocation(lastLocation);
}, 60, 60, TimeUnit.SECONDS);
return START_STICKY;
}
LocationListener locationListenerGps = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateLocation(location);
}
...
}
LocationListener locationListenerNetwork = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateLocation(location);
}
...
}
private void updateLocation(Location readLocation) {
//web service call
String response = WebServiceCalls.updateLocation(readLocation);
//log data in a local Sqlite database
saveLocation(readLocation)
}
My main concern is how to handle the updateLocation call and have it in a separate thread from the main app thread. The scheduleTaskExecutor I belive it's not the way to go. Sometimes, even after I call stopService() the service remains alive, even if I tell the TaskExecutor to shutDown. I can't find another explanation for which the service isn't stoping.
So to recap: I need to send the location each time the listeners receive a new location and resend it every 60 seconds. I also need to be able to stop the service quickly with active threads canceling.
How would you recommend me to handle my case ?
I'd use an IntentService and just use the AlarmManager to fire off intents.
The major advantage of this is that there is no Thread code to worry about as it does its work in the background
UPDATE
Another interesting approach can be found in https://stackoverflow.com/a/7709140/808940
The Service runs the same process as main app, not thread. Also if you want to run service in another process, then you can use the android:process tag.
I'm not sure why you want to call WebService every 60 secs, because 60 secs is too less. Also you should skip calling the WebService when location has not changed, because it requires a network communication and it is a costly operation.
There is no need to use the Executors. You should keep the number of threads as less as possible. To perform a task at particular interval, use AlarmManager to deliver intent at a particular time. Check setRepeating() method for setting the alarm.
Another thing is, you should try to avoid doing any task in Listener. Because there is a timeout of 10 seconds that the system allows before considering the receiver/listener to be blocked and a candidate to be killed. You should use the Handler to perform tasks in the background thread (i.e. whenever you receive the update from listener, add a message to Handler queue and it will be picked when Handler thread is free).
For prevent your service to destroy you can start your service as a foreground service.
And after getting a location from onLocationChanged() method you can use a asynctask for send a location to the webservice so it will not block your UI.
Edit
You can set the minimum time and minimum distance traveled in your requestLocationUpdates method. So I don't think you should use the scheduler task for send location to server. According to the argument about min time and min distance the location manager will check the location. If there is a location changed then it will call onLocationChanged() method with new Location.
Now for your solution about user stays in the same position. you can change some logic to server side like if there is a 1 hour difference between two successive locations location1 and location2 means that user has stayed 1 hour at location1.
You can use a single LocationListener class to listen GPS and NETWORK location.
When you get location in the onLocationChanged() method you can send that location using a asynctask.
After getting the location you can save that location in the preference or database to check weather the GPS and Network provider sending you same location so if you will track then you can save your webAPI call and so you can save some portion of the battery.
You should use AsynchTask:
public class RefreshTask extends AsyncTask<Integer, Void, Integer> {
/**
* The system calls this to perform work in a worker thread and delivers
* it the parameters given to AsyncTask.execute()
*/
public RefreshTask() {
}
protected Integer doInBackground(Integer... millis) {
try{
int waitTime = 0;
while(waitTime<60000){
Thread.sleep(100);
waitTime += 100;
}
//update location here
}catch(InterruptedException e){
}
return 1;
}
/**
* The system calls this to perform work in the UI thread and delivers
* the result from doInBackground()
*/
protected void onPostExecute(Integer result) {
new RefreshTask.execute();
}
}
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.
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 What is the simplest and most robust way to get the user's current location on Android?
EDIT (by asker): This is most of the answer, but my final solution uses a Handler instead of a Timer.
If the user's location is already stable, then getLastKnownLocation will return the current location. I'd call getLastKnownLocation first, look at the timestamp (compare Location.getElapsedRealTimeNanos() with SystemClock.elapsedRealTimeNanos()) then register a listener if the fix is too old.