I'm currently working on implementing a Service
that when requested will perform some work on several parallel threads.
My implementation is based on the ThreadPoolExecutor
class along with a LinkedBlockingQueue
.
As a ground rule, once all tasks are finished and the are no pending tasks in the queue, I would like to stop the service (though the service can later on be started again and follow the same logic).
I have been able to reach the desired outcome using the code below, yet I'm not sure if this approach is correct.
public class TestService extends Service {
// Sets the initial threadpool size to 3
private static final int CORE_POOL_SIZE = 3;
// Sets the maximum threadpool size to 3
private static final int MAXIMUM_POOL_SIZE = 3;
// Sets the amount of time an idle thread will wait for a task before terminating
private static final int KEEP_ALIVE_TIME = 1;
// Sets the Time Unit to seconds
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
// A queue of Runnables for the uploading pool
private final LinkedBlockingQueue<Runnable> uploadQueue = new LinkedBlockingQueue<Runnable>();
// A managed pool of background upload threads
private final ThreadPoolExecutor uploadThreadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
uploadQueue) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (getActiveCount() == 1 && getQueue().size() == 0) {
// we're the last Runnable around + queue is empty, service can be
// safely stopped.
TestService.this.stopSelf();
}
}
};
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// execute a new Runnable
uploadThreadPool.execute(new TestRunnable());
/**
* Indicating that if Android has to kill off this service (i.e. low memory),
* it should not restart it once conditions improve.
*/
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
uploadThreadPool.shutdownNow();
uploadQueue.clear();
super.onDestroy();
}
}
So I've got a few things I'm not yet sure about.
Assuming
onDestroy
was called, is it safe to assume that my implementation will interrupt ALL running threads and will safely clear the pending tasks without somehow interrupting with theThreadPoolExecutor
class implementation? the reason I'm asking is because the queue is associated with the executor, and perhapsshutdownNow
is async and depending on the state of the queue. Is there a better way of doing this?Am I correct implementing this logic inside
onDestroy
? from my experience there are some cases where the service is killed (i.e. low memory) and this callback is not called. Should I perform a similar approach in other places as well?Would it be better to declare my queue & executor class members as static?-- As stated by @TheTwo"Excecutor cannot be re-used once shutdown is called"
.ThreadPoolExecutor
class expects aBlockingQueue
, what are the pros/cons of using other types ofBlockingQueue
implementations (i.e.ArrayBlockingQueue
)?Regarding the way I currently detect when the queue is empty & there are no more pending tasks (specifically inside
afterExecute
callback) -- Is this the best way of doing this? Or can I get an indication that the queue is empty and tasks are finished in another way?
Appreciate any help!