I have an event that fires within a runnable found within a service.
The main runnable is started by a TimerTask. The event within this main runnable should sequentially spawn worker threads.
The threads should work in the sequence that they were spawned and go to completion.
What is the best way to do this in Android?
I have seen info on AsyncTasks, Exexcutor, ThreadPool, etc. but I don't know what is best practice.
I just need a brief example of some way to handle this.
The service class looks something like this:
public onCreate(){
runnable = new Runnable() {
public void run(){
object.addListener(new Listener() {
public void onEvent(Event event) {
// Put Code here to spawn sequential worker threads ...
}
}
}
}
What do I add to the service class in terms of methods and members and what goes in the onEvent? Thank you!
Since you talk about a service, I am assuming that your task will not have to directly interact with a user interface. You can use a ThreadPoolExecutor, which implements the Executor interface. This code fragment will create a thread pool with exactly one thread, this means that all the Runnable instances will execute sequentially. Since this will create an Executor for your tasks only, this method will not block the execution of any other tasks.
Executor tpe = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
tpe.execute(new Runnable() {
@Override
public void run() {
// Do your runnable task
}
});
The Executor will be a member variable of your service and the execute part should be in your onEvent method.
If however you need to interact with a user interface, then you can use the AsyncTask which solves the problem of synchronising calls with the UI thread. The AsyncTask needs to be created on the UI-thread, so in your case this will only work if onEvent is fired by the UI-thread. Also keep in mind that your Runnables will execute in between of any other AsyncTask which your activity might use. So if you flood the AsyncTask queue in your service, then you might end up with task that don't seem to run as they might be in the queue for a long time. Also note that the way AsyncTasks are executed has changed during the history of Android versions:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution. Source
Starting off API level 11, you could also use executeOnExecutor to execute your AsyncTask on the ThreadPoolExecutor we created.
Executor tpe = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
AsyncTask asyncTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
return null;
}
};
asyncTask.executeOnExecutor(tpe, params);
This piece of code is a demonstration of the serialized execution behavior. Create the ThreadPoolExecutor once, then the for loop will add 5 tasks to the ThreadPoolExecutor queue. The Runnable nameless classes will print a message to the console when the tasks starts, then it will sleep a minimum of two seconds before it ends with a message that it just finished.
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
// Our thread pool consists of 1 thread
final Executor tpe = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
for (int i=0; i<5; i++) {
final int index = i;
tpe.execute(new Runnable() {
@Override
public void run() {
System.out.println("Running task: " + index);
try {
// Sleeping at least 2 seconds to pretend to be doing something CPU intensive
Thread.sleep(2000);
} catch (InterruptedException e) {
// lets ignore this for simplicity
}
System.out.println("Finished task: " + index);
}
});
}
}
}
The result output will be:
Running task: 0
Finished task: 0
Running task: 1
Finished task: 1
Running task: 2
Finished task: 2
Running task: 3
Finished task: 3
Running task: 4
Finished task: 4
If we'd for demonstration purposes added an extra Thread to the ThreadPool by replacing the tpe variable initialization with this piece of code:
final Executor tpe = new ThreadPoolExecutor(2, 2, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
Then the output will indicate that more than one task it running simultaneous.
Running task: 0
Running task: 1
Finished task: 1
Finished task: 0
Running task: 2
Running task: 3
Finished task: 2
Finished task: 3
Running task: 4
Finished task: 4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With