4
votes

Here is my code -

public class BackgroundService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        new ServiceThread(startId);
        return START_NOT_STICKY;
    }

    class ServiceThread extends Thread {
        private int startId;

        ServiceThread(int startId) {
            this.startId = startId;
        }

        @Override
        public void run() {
            super.run();
            try {
                Thread.sleep((long) Math.random());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            stopSelf(startId);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

According to this link "Stopping a service", I can/should call stopSelf with received "startId".

However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn't stop the service when you're done processing a start request, because you might have since received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered to onStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.

My question is, what will happen, if I invoke stopSelf with last "startId", and still some earlier start is still not finished. In that case, the startId will match, and according to that document the service will die? All all other earlier "starts" will just be killed?

If the answer is "yes", then what is the best practice to achieve that the service will not be killed until all earlier start is not finished.

3
Is it bad or wasteful to only call stopSelf once instead of multiple times? I mean the same Service instance will only exist once per app process...IgorGanapolsky

3 Answers

3
votes

I just run into exactly the same problem yesterday. And I think I found answer in the javadoc of Service.stopSelfResult(int startId) method.

If you may end up processing IDs out of order (such as by dispatching them on separate threads), then you are responsible for stopping them in the same order you received them.

1
votes

A solution is to maintain a boolean hash map with the start ID as the key. Then in your worker threads, instead of calling stopSelf(int) call a custom method which does the following:

Set the hash map entry to true using the start ID as the key. Iterate through the keys in ascending order and call stopSelf(key) until you encounter a false entry.

1
votes

This is a subtle problem - I'm not sure any of the solutions above are adequate/efficient. The solution I use is based on a class called ThreadPoolService, which extends Service and operates as follows:

  • It defines HashSet<Integer> that stores the startId passed by onStartCommand()
  • It defines an int field called mLastStartId that stores the most recent startId passed by onStartCommand()
  • It defines an ExecutorService initialized by either newCachedThreadPool() or newFixedThreadPool()
  • It defines a beginIntentProcessing() method that adds the startId parameter to the HashSet and records the latest startId in mLastStartId
  • It defines an endIntentProcessing() method that removes the startId parameter from the HashSet and returns if the HashSet is non-empty. If the HashSet is empty, however, it calls the stopSelf() method on the Service superclass, passing in the mLastStartId.

I've omitted some details of the solution, but it's efficient and solves the underlying problem described above. I'll be covering this topic (and many others) in my upcoming MOOCs on Android concurrent programming, which are described at http://www.coursera.org/course/posaconcurrency and http://www.coursera.org/course/posacommunication.

Doug