Details:
I have a service that needs to do the following:
- listen constantly for GPS locations, with 50 meters as parameter (works fine) and send it to a server
- each 60 seconds, if no GPS location was read, start listening for a Network location and send it to the server
This may sound weird, but this is the project requirement. So the user is constantly being tracked using GPS. When he stops, or GPS is not locked, or is inside a building, every 60 seconds start a quick Network location read, and send this location to the server. As the service uses the same thread as the main app, each server update is done in its own thread. And another important thing: each location read should be sent one after another, so for instance if the user is driving, and multiple reads are done, each should be send to the server, after the previous one has been sent. That's why I decided to use ScheduledExecutorService as I can submit threads and they will be execute one after another.
Here is the code:
private ScheduledExecutorService scheduleTaskExecutor;
Handler locationHandler = new Handler();
private Location lastNetworkLocation;
#Override
public void onStartCommand() {
scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();
//prepare to execute Network reading every 60 seconds
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
initNetworkReadings();
//usually a network location read is done almost instantly
//however after 5 seconds I check if a location has been read by the Network listener inside the locationRunnable
locationHandler.postDelayed(locationRunnable, 5000);
}
}
}, 60, 60, TimeUnit.SECONDS);
locationRunnable = new Runnable() {
#Override
public void run() {
if (lastNetworkLocation !=null){
//send location to the server if valid
}
lastNetworkLocation = null;
}
}
}
private void initNetworkReadings() {
locationManager.removeUpdates(locationListenerNetwork);
try {
isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch (Exception ex) {
}
if (isGpsEnabled) {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
}
}
LocationListener locationListenerNetwork = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
lastNetworkLocation = location;
// stop listening for Network locations
locationManager.removeUpdates(locationListenerNetwork);
}
...
}
Each time I read a GPS location I add it to the threads queue as:
scheduleTaskExecutor.submit(new Runnable() {
#Override
public void run() {
updateLocationOnServer(readLocation);
}
});
The problem I have is that the Network location listener never gets onLocationChanged() called,when I use it like in the code above, in the Runnable. BUT if I add on service start, the initNetworkReadings(), I get onLocationChanged() fired right away. So I believe it has something to do with being used in scheduleAtFixedRate.
What do you think it could be the problem ? Anything bad in the way I thought of the workflow ?
What if you try to set up your repitition with a Handler and a Runnable instead of scheduleAtFixedRate()?
Handler h = new Handler();
Runnable run = new Runnable(){
//Do your repeititive work here!
h.postDelayed(run, 60 * 1000);
};
h.post(run);
Same problem,
I have a background service that detect locations.
If network is avaible, it uses networks, otherwise it uses gps.
It works well with many smartphones
( nexus s 4.1, galaxy nexus 4.2, galaxy note) , but with Galaxy s3 jb (4.1) network location never rises any location.
Same code with gps locations, works just fine.
Related
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 need to find the user position with a timeout, then I wrote a code like this
Start a LocationListener
GPSLocationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
_timerGPSLocation.cancel();
}
#Override
public void onStatusChanged(String provider, int status, Bundle b) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDistance, _GPSLocationListener);
`
Setup a timer for the timeout
_timerGPSLocation = new Timer();
_timerGPSLocation.schedule( new TimerTask() {
#Override
public void run() {
_locationManager.removeUpdates(_GPSLocationListener);
}
}, (long)(timeout*1000) );
I think that doing this (trying to read coordinates and setup a timer with timeout) for many times can let the GPS contact some satellites and give me the right location.
There is a better way for doing this?
Calling _locationManager.removeUpdates on the timeout will remove all contacted satellites?
EDIT:
My goal is to read the GPS at regular intervals (5 minutes). I need also to set a timeout while try to get the location using GPS read. if no location is read after the timeout I need to stop the location listener. I've achieved this using the code liste here.
Now my question is if removing the LocationListener because it's go in timeout will cause the loss of the "acknowledgement" between the GPS and the satellite.
Scheduling a timer would work for what you're trying to do. Similarly you could use a handler and pass it a thread or a Looper object. As in
Handler h = new Handler();
h.postDelayed(Runnable r, long millis);
With that you could also post a runnable, i.e thread or looper at a time delay to cancel location updates.
EDIT: postDelayed is used for a runnable to run after a certain delay. postAtTime posts at a certain uptime of the program
Scenario
Step 1: Init the location manager to read GPS locations every 50 meters:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 50, locationListenerGps);
Step 2: each time a location is read:
#Override
public void onLocationChanged(Location location) {
new Thread(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
}).start();
}
Step 3: on sendLocation there are a few things I do:
query the local sqlite database for failed to send records
if any, send them together together with the current location to a web service
if none, send only the current location
if sending failed (mostly because of data connectivity), insert location in database for future readings
if sending succeed, delete all rows from the database
The problem
All this is done in background in a service. For each sendLocation call I make a new thread. While connectivity is ok, everything works fine. But, when sending fails and the user is driving, the location read happens very often and there are big chances that there are 2-3 threads all trying to send the same unsent locations. If Thread1 receives the list and tries to send it, Thread2 and Thread3 should not be able to read it and try to send it as Thread1 may send it successfully. How can I prevent this from happening ? How can I make sure Thread2 does not read the list ?
From what I am thinking now, I could add a new field in the table "processing" and for all the rows retrieved for sending, update the field to true. In this case Thread2 will only get the processing=false rows. Is this a solution ? Any other recommendations ? I still believe that there is a slight change for Thread2 to get the data, while Thread1 is updating processing... Thanks.
Later edit: Extra thoughts and ideas I have tried this approach
private ExecutorService threadPool;
#Override
public void onCreate() {
scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();
threadPool = Executors.newSingleThreadExecutor();
//also need to send location every 60 seconds if no other location was read
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
sendLocation(lastLocation);
}
}, 60, 60, TimeUnit.SECONDS);
}
#Override
public void onLocationChanged(Location location) {
threadPool.execute(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
});
}
#Override
public void onDestroy() {
threadPool.shutdownNow();
}
From what I read, this threadPool should force threads to execute one after another, right ? (even I do have a feeling I misunderstood its purpose) If so, what happens if I get no connectivity for an hour ? For each location read, a new thread is added... but how long does this thread last ? I am concern what happens if the user is driving really fast, I could get locations read every 1-2 seconds, would this mechanism hold my web access in a queue, one thread after another ?
In another order of thoughts, what if onCreate method of the service I make a new thread. Something like:
#Override
public void onCreate() {
new Thread(new Runnable() {
#Override
public void run() {
startLocationListener();
}
}).start();
}
and in startLocationListener() I start GPS location read. Will onLocationChanged be executed on this thread and won't interfere with my UI thread ?
Would it be wiser to use a Service that runs in its own thread ? So I won't have to worry about threading ?
Using the current approach, the app does the job but there is something wrong happening, randomly and can't figure out the reason: one of my activities binds to the service to receive updates, I carefully unbind it when the app gets onPause... but sometimes the service keeps running, as I can see its notification icon displayed. I will investigate this more, but I need to settle a strong/reliable way of handling location reading and sending.
Later later edit
How about this approach:
private ExecutorService scheduleTaskExecutor;
#Override
public void onCreate() {
scheduleTaskExecutor = Executors.newSingleThreadScheduledExecutor();
//also need to send location every 60 seconds if no other location was read
scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
sendLocation(lastLocation);
}
}, 60, 60, TimeUnit.SECONDS);
}
#Override
public void onLocationChanged(Location location) {
scheduleTaskExecutor.submit(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
});
}
#Override
public void onDestroy() {
scheduleTaskExecutor.shutdownNow();
}
So let me get this straight: you want to send locations one after the other from a background thread. A simple scheme to achieve this would be (similar to your edited code, but I don't see the reason for ScheduledExecutor):
private ExecutorService exec;
#Override
public void onCreate() {
exec = Executors.newSingleThreadExecutor();
}
#Override
public void onLocationChanged(Location location) {
exec.submit(new Runnable() {
#Override
public void run() {
sendLocation(location);
}
});
}
#Override
public void onDestroy() {
exec.shutdownNow();
}
What this does under the hood is basically to create a background thread and a queue of tasks. Every time a location is read a new task is put into the queue. The thread continuously polls the queue and executes the tasks in order.
For each sendLocation call I make a new thread.
Why?
But, when sending fails and the user is driving, the location read happens very often and there are big chances that there are 2-3 threads all trying to send the same unsent locations.
This is why I asked "Why?" above.
If Thread1 receives the list and tries to send it, Thread2 and Thread3 should not be able to read it and try to send it as Thread1 may send it successfully. How can I prevent this from happening ? How can I make sure Thread2 does not read the list ?
IMHO, by not having Thread2 and Thread3 in the first place. Use a single thread at a time, that sends all unsent data. That is probably a long-lived thread, working off of a work queue (coupled with some sort of timer mechanism to handle the case where you failed to update before and wish to make sure you try again after X period of time, if no other events forced you to try sooner than that). I don't see why you would need more than that to achieve your aims.
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();
}
}
i have to get my location inside service from another class by this code
Mylocation mylol = new Mylocation();
private void locationClick() {
mylol.getLocation(this, locationResult);
mylol.cancelTimer();
// runDialog(3);
}
public LocationResult locationResult = new LocationResult(){
;
#Override
public void gotLocation(final Location location){
//Got the location!
double MyFinalLat=location.getLatitude();
double MyFinalLon=location.getLongitude();
Myloc=MyFinalLat+","+MyFinalLon;
Toast.makeText(getBaseContext(),"Your current location"+Myloc,
Toast.LENGTH_SHORT).show();
};
};
}
but i should make the gpss take his time to find locattion
how can i make my service slaaps for 20 secons for exampl??
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
my_button.setBackgroundResource(R.drawable.defaultcard);
}
}, 2000);
The code:
Thread.sleep(20000);
Will make the current thread sleep for 20 seconds. From an Activity, this would probably cause a force close, because it effectively make your process seem like it's locked up (I assume). In a Service, as you describe, you may be alright though.
BTW the 20 seconds isn't incredibly precise, as noted in the docs.