5

I have 2 AsyncTasks running in a fragment.
The AsyncTasks are defined in a different class and not inner private classes of the fragment.
The problem I have is that now it came up that I need for AsyncTaskX to wait until AsyncTaskY has finished its onPostExecute
How can I solve this?
I was thinking of using a countdownlatch but AsyncTaskY is in a different class and I am not sure what is the best way to code this?
Is there a way to check if an AsyncTask has finished completely?

Update:
I was wondering is task.execute().get() returning after the onPostExecute?

Update 2:
Is calling CountDownLatch.countDown() from UI thread safe?

Jim
  • 18,826
  • 34
  • 135
  • 254
  • 1
    Can you pass the same `CountDownLatch` instance to both tasks' constructors? – Andy Turner Apr 20 '16 at 20:33
  • You could use broadcasts to notify a receiver in the fragment when each is complete. – Dan Harms Apr 20 '16 at 20:34
  • You will get a response back from the first AsyncTask. On the callback of your First Async you can just call the second AsyncTask. – Ahmed Apr 20 '16 at 20:34
  • @AndyTurner: I could change the code to pass that. But I was wondering if there was a way to check if the AsyncTask has finished from its standard API. I have a reference to it, but I can only check if it has been cancelled – Jim Apr 20 '16 at 20:36
  • @Ahmed:Won't be so efficient because the other task is doing also some "heavy" work in parallel and if I serialize them I will make it slower. I wanted to keep parallelization and only serialize what need to be serial – Jim Apr 20 '16 at 20:37
  • Be aware AsyncTasks are executed serially on a single background thread (from API 11) – Maxim G Apr 20 '16 at 20:43
  • @AndyTurner:Can I call `signal` of the latch in UI thread? – Jim Apr 20 '16 at 20:46
  • Use `executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)` to execute in parallel – Dan Harms Apr 20 '16 at 20:47
  • @MaximG:First time I hear about this. If that is true then any coordination would deadlock – Jim Apr 20 '16 at 20:59

4 Answers4

1

Is it really difficult to determine this without seeing your code. One dirty solution would be to add a static boolean and then add a recursive timer. This is not the best programming technique, but from what I read it would certainly work.

Create a static boolean in any class

static boolean onPostExecuteFinished;

in the the AsyncTask that needs to be finished first set it to true

ClassName.onPostExecuteFinished = true;

In the class that needs to wait you make a recursive method waiting for it to finish. I recommend using a handler.

public void nameOfRecursiveMethodHere()
Handler handler = new Handler()

handler.postDelated(new runnable........{
if (ClassName.onPostExecuteFinished) {
//Great it is finished do what you need
} else {
//Not finished call this method again
nameOfRecursiveMethodHere();
}

}),(put how often you want it to check in milliseconds here);
fsebek
  • 389
  • 2
  • 9
  • That would work, but it can't be a real solution to something that should be a frequent problem, i.e. synchronize async tasks – Jim Apr 20 '16 at 20:43
  • onPostExecute is when the AsyncTask is finished, so start the second AsyncTask in the first onPostExecute? – fsebek Apr 20 '16 at 20:47
  • I will lose concurrency. I think it is better to serialize only what need to be serial – Jim Apr 20 '16 at 20:57
  • I certainly agree. However, if they are dependent on each other they should not be run asynchronously. If you do not want to lose concurrency, the best alternative I can thing of without seeing you code is having a timer, like I mentioned above. – fsebek Apr 20 '16 at 21:04
  • Check the discussion with MinaSamy – Jim Apr 20 '16 at 21:06
  • Cant you move the AsyncTaskY into AsyncTaskX? Why does it have to be a separate thread. – fsebek Apr 20 '16 at 21:11
  • Because they have things that is faster to do in parallel than serial – Jim Apr 20 '16 at 21:33
  • @Jim Well I would agree, but running it like that will not speed up anything since you will be waiting for it to finish anyways. – fsebek Apr 20 '16 at 21:42
  • Not really. Only a specific part needs to be serialized. There is no reason to serialize more than one needs – Jim Apr 21 '16 at 11:15
1

Rather than using 2 AsyncTasks I would suggest to use RxJava and RxAndroid. Concurrency is done much easier there.

For example you can do the following to sync your async Jobs:

Observable<Object1> job1 = Observable.fromCallable(() -> yourCall2());
Observable<Object2> job2 = Observable.fromCallable(() -> yourCall2());

Observable.combineLatest(job1, job2, (object1, object2) -> yourMethod())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(...);

This is only a small, incomplete example but should show how to achieve what you want. I've used Lambda Expressions to shorten the Code. This is done by using Retrolambda. You can achieve the same with the zip Operator.

dipdipdip
  • 2,326
  • 1
  • 21
  • 31
1

As @Andy-Turner commented, using a CountDownLatch is possible, also you can use a Semaphore to signal the execution of a task from another.

please check this link: http://tutorials.jenkov.com/java-concurrency/semaphores.html

Mina Wissa
  • 10,923
  • 13
  • 90
  • 158
  • interesting question :) but why would you do that? if you need to sync the two async tasks on the UI thread level, you can just implement that with broadcast receivers or content observers. no need to introduce more complexity with Java's synchronization methods. – Mina Wissa Apr 20 '16 at 21:01
  • No. AsyncTaskX is running in the background thread in `doInBackground` and at some point needs something that is ready *after* AsyncTaskY has finished its `onPostExecute`. So is calling `countDown` from the `onPostExecute` of AsyncTaskY in order for `AsyncTaskX` to continue the `doInBackgound` safe? – Jim Apr 20 '16 at 21:05
1

AsyncTasks are executed serially on a single background thread (from API 11).

A little test:

private static class AsyncTaskX extends AsyncTask<Void, Void, Void>{
    @Override
    protected Void doInBackground(Void... params) {
        for (int i = 0; i < 10; i++){
            Log.i("test order", "ping X "+ i);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return null;
    }
}

private static class AsyncTaskY extends AsyncTask<Void, Void, Void>{
    @Override
    protected Void doInBackground(Void... params) {
        for (int i = 0; i < 10; i++){
            Log.i("order", "ping Y" + i);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

Logs:

test order: ping X 0
test order: ping X 1
test order: ping X 2
test order: ping X 3
test order: ping X 4
test order: ping X 5
test order: ping X 6
test order: ping X 7
test order: ping X 8
test order: ping X 9
order: ping Y0
order: ping Y1
order: ping Y2
order: ping Y3
order: ping Y4
order: ping Y5
order: ping Y6
order: ping Y7
order: ping Y8
order: ping Y9

So long running worker can block others.

task.execute().get() will return before the onPostExecute and you will block UI thread.

Check general information about processes and threads and some gotchas.

Time of execution also matters, because AsyncTask should only be used for tasks/operations that take quite few seconds.

Maxim G
  • 1,479
  • 1
  • 15
  • 23
  • AsyncTaskX and AsyncTaskY share a `CountDownLatch`. For both I instantiate them and call `execute`. In AsyncTaskX I do some work in the `doInBackground` and in the same function I call `await` on the CountDownLatch. So at that point the thread blocks. In AsyncTaskY I do some work in the `doInBackground` then I move on to `onPostExecute` where I eventually call `countDown` on the same countdown latch that AsyncTaskX is waiting on. **Question:** Why don't I get a deadlock if the async tasks are executed serially from the same thread? – Jim Apr 21 '16 at 19:26
  • Code is trivial. Just pass a CountDownLatch to 2 AsyncTasks and call execute. The only explanation I can give is that I get the AsyncTaskY scheduled before AsyncTaskX constantly – Jim Apr 21 '16 at 19:35
  • Tested on 1 device only if that matters – Jim Apr 21 '16 at 19:36
  • In this case then I get a scheduling that does not cause a deadlock in the device I test. I did not understand what you mean here: `If you have serial background tasks use HeandlerThread or your own Thread with Looper and Handlers.` You mean either make one or both of the AsyncTasks into some other construct? `HeandlerThread or your own Thread with Looper and Handlers.` Why not a plain Thread and Runnable? – Jim Apr 21 '16 at 20:52
  • I had written it before you explained some details in comments. The idea was do not block UI thread by serial AsyncTasks, use HeandlerThread for that purpose. Without your code hard to say why it works. – Maxim G Apr 21 '16 at 21:09
  • But why would I prefer the HandlerThread over plain `Thread` and Runnable? – Jim Apr 22 '16 at 17:25
  • At first it sounds like you wanted serial tasks in the background. Cleaned that sentence. – Maxim G Apr 22 '16 at 17:33
  • I have an existing AsyncTask that does something complicated. I had a new need to do something also heavy. This new heavy task has at its finishing steps a "dependency" on the first task to finish. 1) If I use the HandlerThread do I get the serial problems? 2) Why is it better than using a plain thread? – Jim Apr 22 '16 at 17:36
  • You can use standard java threads, just keep in mind that Android UI toolkit is not thread-safe. To make life a bit easier with UI synchronization google team created helpers. HandlerThread is like UI Thread with own Looper and message queue, so all messages will be handled serially. – Maxim G Apr 22 '16 at 18:13