21

I have 3 AsyncTasks and 1 ProgressBar. I want when any of task executes, the progress bar is visible and when all of them finish, the progress bar is invisible.

In Java, there is ExecutorService::isTerminated to check if all runnables finished but Android doesn't have it.

Update: 3 tasks execute at the same time.

Figure. enter image description here

emeraldhieu
  • 9,380
  • 19
  • 81
  • 139

10 Answers10

24

Nice graphic. But I am afraid there is no build in mechanism for this. You'll have to implement it by yourself. There are few solutions you could use -

  1. Keep a reference to all 3 task. When task finishes check if the other two tasks are finished too, if yes than close the progress dialog if no wait for some other task to finish and check again. Make sure you free the references when you're done.
  2. If you don't want to keep a reference store a counter. When the task finishes, increment the counter and check if it's equal to 3. If all tasks finished and you are done. If you implement this make sure to synchronized the access to the counter.
Brian
  • 823
  • 6
  • 14
Mojo Risin
  • 8,136
  • 5
  • 45
  • 58
7

Try using AsyncTask.getStatus(). This works perfectly fine. Refer below sample code.

 List<AsyncTask<String, String, String>> asyncTasks = new ArrayList<AsyncTask<String, String, String>>();
 AsyncTask<String, String, String> asyncTask1 = new uploadTask().execute(string);
 AsyncTask<String, String, String> asyncTask2 = new downloadTask().execute(string);
 AsyncTask<String, String, String> asyncTask3 = new createTask().execute(string);
 asyncTasks.add(asyncTask1);
 asyncTasks.add(asyncTask2);
 asyncTasks.add(asyncTask3);

You can later loop the AsyncTaskList and find each of the tasks' status as below.

 for(int i=0;i<asyncTasks.size();i++){
      AsyncTask<String, String, String> asyncTaskItem = (AsyncTask<String, String, String>)asyncTasks.get(i);
      // getStatus() would return PENDING,RUNNING,FINISHED statuses
      String status = asyncTaskItem.getStatus().toString();
      //if status is FINISHED for all the 3 async tasks, hide the progressbar
 }
Jagadeesh
  • 883
  • 1
  • 10
  • 26
  • I did it without the names, but it worked out perfectly.. Thanks! I did remove the items from the ArrayList afterwards (when status == finished) instead of just checking the status. This would make it easier on larger sets of asyncTasks like my set of 30+ tasks.. – Mathieu Brouwers May 05 '15 at 12:08
2

A simple workaround would be to use three boolean variables one each for each AsyncTask and then check them accordingly.

A better approach would be to create a separate class that extends AsynTask and defines a callback interface which is fired in onPostExecute.

PravinCG
  • 7,688
  • 3
  • 30
  • 55
1

create a field to hold all tasks:

private ArrayList<HtmlDownloaderTask> mTasks;

Start your tasks this way:

HtmlDownloaderTask = new HtmlDownloaderTask(page.getHtml());
task.execute(page.getUrl());
//if you want parallel execution try this:
//task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,page.getUrl());
mTasks.add(task);

on the onPostExecute of MyAsyncTask:

int unfinishedTasks = 0;
for (HtmlDownloaderTask myDT : mTasks){
    if(!(myDT.getStatus() == AsyncTask.Status.FINISHED)){
        unfinishedTasks++;
    }
}
if (unfinishedTasks == 1){
    //We are all done. 1 Because its the current one that hasnt finished post execute
    callWhateverMethod();

}
Iraklis
  • 2,762
  • 1
  • 24
  • 31
1

Well as you do know when an AsyncTask ends (when onPostExecute gets called): one solution could be to create a method setProgressBarVisible() that keeps a counter and when first called sets visible, and a method setProgressBarInvisible() that decreases the counter and when zero sets the progress bar invisible.

Dinith Rukshan Kumara
  • 638
  • 2
  • 10
  • 19
MrJre
  • 7,082
  • 7
  • 53
  • 66
0

I would simply notify it at onPostExecute(), refer to onPostExecute and 4 steps in the document for detail and you can use EventBus to do some subscribe things.

Dinith Rukshan Kumara
  • 638
  • 2
  • 10
  • 19
Olivia Liao
  • 375
  • 3
  • 7
0

A official support of CompletableFuture was introduced since API level 24.

It's also available in Java 8 here.

Can use simply use something like:

taskA.thenCombine(taskB).thenCombine(taskC)
superuser
  • 768
  • 7
  • 9
0

This is a common question when you want to run a bunch of AsynTasks on a THREAD_POOL_EXECUTOR. It's much more faster than if you just call .execute() and all your tasks are done one by one. So if you have multiple jobs and objects are not depending on each other states - try to run on a thread pool.

But the question is: how do I know that all of my tasks are done?

There is no built in methods in AsyncTask so you should do a little workaround.

In my case I added a static Hashmap field to my Asynctask class to keep track of all started and finished tasks. As a bonus of a map I can always know which task is currently in progress.

    private static HashMap<Uri, Boolean> mapOfAttachmentTasks = new HashMap<>();

and ad simple three methods to access this map. Important: they should be synchronized

public static synchronized void addTask(Uri uri){
    mapOfAttachmentTasks.put(uri, true);
}

public static synchronized void removeTask(Uri uri){
    mapOfAttachmentTasks.remove(uri);
}

public static synchronized boolean isTasksEmpty(){
    return mapOfAttachmentTasks.isEmpty();
}

You want to add a new item to the tracking Map in an AsyncTask constructor and remove it in onPostExecute():

public AttachmentTask(Uri uri) {
    this.uri = uri;
    addTask(uri);
}

@Override
protected void onPostExecute(Attachment attachment) {
    removeTask(uri);

    if(isTasksEmpty())
        EventBus.getDefault().post(new AttachmentsTaskFinishedEvent(attachment));
}

Everytime a task is finished it calls onPostEexecute and you check if it was the last task. If there is no tasks left - send a signal that you're done.

Now, here I used EventBus to send event to my Fragment but you can use a callback. In this case you should create an interface with callbackMethod, your Fragment (any of your UI components which are waiting for the event) should implement this interface and have that method. Then in AsyncTask constructor you get your Fragment as an argument and keep a reference to it, so you can call it's callback method when everything is done.

But I dont like such approach. First you need to keep the reference of your Fragment (or any other UI) in a WeakReference wrapper becasue you will get a memory leak when your fragment is dead (but still kept in memory becasue your AsyncTask has it's reference). Also you would need to make a lot of checks and it will look something like that:

private boolean isAlive() {
    return mFragmentWeakReference != null
            && mFragmentWeakReference.get() != null
            && mFragmentWeakReference.get().isAdded()
            && mFragmentWeakReference.get().getActivity() != null
            && !mFragmentWeakReference.get().getActivity().isFinishing();

yep, in production you should be a little paranoic and do all these checks :)

That's why you can use EventBus and if your UI is dead - whatever.

Kirill Karmazin
  • 6,256
  • 2
  • 54
  • 42
0

:-? i think it's just a trick. you will return some message at onPostExecute of each Asyntask and compare it. (this message can contain a time, for example)

hqt
  • 29,632
  • 51
  • 171
  • 250
-1

try this, maybe can help you...

            final ImageUploader _upload = new ImageUploader();
            _upload.setValue(getApplicationContext(), _imagepath, _urlPHP);
            _upload.execute();
            Runnable _run;
            Handler _h2;
            _run = new Runnable() {
                public void run() {
                    _h2 = new Handler();
                    _h2.postDelayed(this, 1000);
                    Toast.makeText(getApplicationContext(), "not finished", Toast.LENGTH_LONG).show();

                    if (_upload.getStatus() == AsyncTask.Status.FINISHED) {
                        Toast.makeText(getApplicationContext(), "finished", Toast.LENGTH_LONG).show();
                        _h2.removeCallbacks(_run);

                    }
                }
            };
            _h2 = new Handler();
            _h2.postDelayed(_run, 1);
Ferdiyan Syah
  • 404
  • 4
  • 6