5

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 ?

Alin
  • 14,809
  • 40
  • 129
  • 218

4 Answers4

4

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

Community
  • 1
  • 1
Moog
  • 10,193
  • 2
  • 40
  • 66
  • And what about the call onLocationChanged? Does it have its own thread or fire an IntentService inthere too ? I also need to know if the call to the web was successful or not. This part I don't think it would be to easy to make with an IntentService. I believe there should be a broadcast sent, right ? – Alin Jul 09 '12 at 21:08
2
  • 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).

Karan
  • 12,724
  • 6
  • 40
  • 33
1

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

  1. 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.

  2. You can use a single LocationListener class to listen GPS and NETWORK location.

  3. When you get location in the onLocationChanged() method you can send that location using a asynctask.
  4. 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.
Dharmendra
  • 33,296
  • 22
  • 86
  • 129
  • As you can see there are reads from both gps and network and also sending on schedule. This means I vould have 3 calls for the asynctask in the same time. How would I cancel them? – Alin Jul 10 '12 at 07:50
  • I must send the position even if the driver stays in the same position as this is part of project specifications. So I can't change this. However I do not see asynctask as a good solution for my case. Imagine I can receive new location every 1 second if the user is driving around. This means every 1 second I must fire the asynctask so I could have like 2-3 tasks running... How can I cancel them when the service stops? – Alin Jul 10 '12 at 11:13
  • Just think about the case that you want to send location every 1 secs. You want to handle your webAPI call at every 1 seconds(not every time but in case of successive location changed). If you cancel the last one and if you update the new one then ofcurse the last location will be ignored. – Dharmendra Jul 10 '12 at 12:05
  • And ofcurse you can cancel the Asynctask but this is not as much simple. you will have to track it by variables. – Dharmendra Jul 10 '12 at 12:07
  • Nah, nothing to do just create a asynctask each time when location changed and execute it. After finish the process it will finish automatically. you will not have to track for all asynctasks. – Dharmendra Jul 10 '12 at 12:13
  • Wouldn't be easier to just use a service that runs in its own thread ? – Alin Jul 10 '12 at 12:37
  • In the end I used this approach. I am sure there are several ways to deal with this, with IntentService or service in own process, but for my request, using a simple new task on each call does the job. – Alin Jul 16 '12 at 09:30
  • Also, for generic repetative tasks, usage of handler.postDelayed(...) to trigger ASynchTask at given period of time might be useful. – Gökhan Barış Aker Jul 18 '12 at 08:03
-1

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();
    }


}
user1404512
  • 66
  • 2
  • 8
  • Do not use `AsyncTask` for this type of problem. Depending on API-Version this will block all other AsyncTasks. – chrulri Jul 15 '12 at 23:58