Wait for multiple AsyncTask to complete
You could also simply decrement a counter in a shared object as part of onPostExecute
. As onPostExecute
runs on the same thread (the main thread), you won't have to worry about synchronization.
UPDATE 1
The shared object could look something like this:
public class WorkCounter {
private int runningTasks;
private final Context ctx;
public WorkCounter(int numberOfTasks, Context ctx) {
this.runningTasks = numberOfTasks;
this.ctx = ctx;
}
// Only call this in onPostExecute! (or add synchronized to method declaration)
public void taskFinished() {
if (--runningTasks == 0) {
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(this.ctx);
mgr.sendBroadcast(new Intent("all_tasks_have_finished"));
}
}
}
UPDATE 2
According to the comments for this answer, OP is looking for a solution in which he can avoid building a new class. This can be done by sharing an AtomicInteger
among the spawned AsyncTask
s:
// TODO Update type params according to your needs.
public class MyAsyncTask extends AsyncTask<Void,Void,Void> {
// This instance should be created before creating your async tasks.
// Its start count should be equal to the number of async tasks that you will spawn.
// It is important that the same AtomicInteger is supplied to all the spawned async tasks such that they share the same work counter.
private final AtomicInteger workCounter;
public MyAsyncTask(AtomicInteger workCounter) {
this.workCounter = workCounter;
}
// TODO implement doInBackground
@Override
public void onPostExecute(Void result) {
// Job is done, decrement the work counter.
int tasksLeft = this.workCounter.decrementAndGet();
// If the count has reached zero, all async tasks have finished.
if (tasksLeft == 0) {
// Make activity aware by sending a broadcast.
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(this.ctx);
mgr.sendBroadcast(new Intent("all_tasks_have_finished"));
}
}
}
You should use a CountDownLatch. Here the documentation with examples: java.util.concurrent.CountDownLatch
Basically you give a reference of CountDownLatch to your threads, and each of them will decrement it when finished:
countDownLatch.countDown();
The main thread will wait on the termination of all threads using:
countDownLatch.await();
First, add this class to your project
public abstract class MultiTaskHandler {
private int mTasksLeft;
private boolean mIsCanceled = false;
public MultiTaskHandler(int numOfTasks) {
mTasksLeft = numOfTasks;
}
protected abstract void onAllTasksCompleted();
public void taskComplete() {
mTasksLeft--;
if (mTasksLeft==0 && !mIsCanceled) {
onAllTasksCompleted();
}
}
public void reset(int numOfTasks) {
mTasksLeft = numOfTasks;
mIsCanceled=false;
}
public void cancel() {
mIsCanceled = true;
}
}
Then:
int totalNumOfTasks = 2; //change this to the number of tasks that you are running
final MultiTaskHandler multiTaskHandler = new MultiTaskHandler(totalNumOfTasks) {
@Override
protected void onAllTasksCompleted() {
//put the code that runs when all the tasks are complete here
}
};
Then in each task - when completed, add the line: multiTaskHandler.taskComplete();
Example:
(new AsyncTask<Void,Void,Void>() {
@Override
protected Void doInBackground(Void... voids) {
// do something...
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
multiTaskHandler.taskComplete();
}
}).execute();
You can use multiTaskHandler.cancel()
if you want to cancel the code that runs when all the tasks have completed. For instance - if you have an error (don't forget to also cancel all the other tasks).
* This solution will not pause the main thread!