1

I'm working an Android app that has to make server request and then perform actions when the request is completed. Here's some pseudo code to help explain the situation:

makeRequest(new SomeTask{
    onDone() {
        // Do actionB with queue
    }
});

// Do actionA with queue.  Must be execute first!!

Here's the implementation of makeRequest in pseudo code:

makeRequest(SomeTask task) {
    if(canDoOptimization) { // if true, don't need to make request

        // It's a bad idea to execute this immediately.
        // Wish I could wait until the current thread of execution was done...
        task.onDone();
        return;
    }

    asyncTask = new AsyncTask<SomeTask, Void, Void>() {
        doInBackground(SomeTask... task) {
            // Make server request...

            task.onDone();
        }
    }
    asyncTask.execute(task);
}

Usually actionA happens before actionB as expected, but in cases where we can avoid a network requests, SomeTask.execute is called immediately. This causes actionB to occur before actionA, which is bad. Is there any way I can guarantee this doesn't happen?

I've run into this situation several times in javascript. In those cases, I would wrap the SomeTask.execute call with a setTimeout or setImmediate to maintain the proper async semantics.

For clarity, here's an example of the same bug in JavaScript: https://gist.github.com/xavi-/5882483

Any idea what I should do in Java/Android?

Xavi
  • 20,111
  • 14
  • 72
  • 63
  • Why can't you call action A before makeRequest? –  Jun 27 '13 at 19:31
  • That is a possibility, but it would require significant refactoring. Also, I was hoping there was a general solution so I wouldn't anyways have to be mindful of this issue. – Xavi Jun 27 '13 at 21:42
  • setting a timeout seems like a very hacky solution to this – Kristopher Micinski Jun 27 '13 at 22:06
  • Right, in Java it probably is. But in JS I feel this is pretty standard use case for `setTimeout`. It's so common that Node.js supplies an even more optimized version called `process.nextTick`. – Xavi Jun 27 '13 at 22:27

3 Answers3

1

Welcome to world of synchronization. Mutex or lock objects are often used for that purpose. Is there a Mutex in Java?

your B task should wait on mutex which is to be signaled by task A upon its completion. That will ensure proper execution order where A task will finish before B.

Community
  • 1
  • 1
Asaf Sh.
  • 49
  • 2
1

Always put task.onDone() in the AsyncTask, even if it doesn't have to make a request.

makeRequest(SomeTask task) {

    asyncTask = new AsyncTask<SomeTask, Void, Void>() {
        void doInBackground(SomeTask... task) {

            if(canDoOptimization) { // if true, don't need to make request

                // It's a bad idea to execute this immediately.
                // Wish I could wait until the current thread of was done...
                task.onDone();
                return;
            } else {
                // Make server request...

                task.onDone();
            }
        }
    }
    asyncTask.execute(task);
}
Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
jimbo
  • 11,004
  • 6
  • 29
  • 46
  • That makes a lot of sense. Unfortunately I over simplified my example. The `canDoOptimization` check and the actual network call occur at different places. The `SomeTask` object gets passed around quite a bit. Perhaps I can just wrap the `task.onDone()` call in a dummy `AsyncTask`? – Xavi Jun 27 '13 at 23:22
  • A dummy AsyncTask is worth a shot. I'm not actually an Android expert, I was taking a shot in the dark :) – jimbo Jun 28 '13 at 02:28
0

Why can't you just switch the order of things?

// Do actionA with queue.  Must be execute first!!

makeRequest(new SomeTask{
onDone() {
    // Do actionB with queue
});

If actionA is asynchronous as well and performed on a separate AsyncTask, you can call makeRequest(...) on actionA's AsyncTasks's onPostExecute() method.

And btw, since Android Honeycomb version, AsyncTasks are ran on the same thread, meaning if you have several tasks they can block each other. This is fixed by specifying that the AsyncTsak should run in a thread pool:

if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
  asyncTask.execute();
}
dors
  • 5,802
  • 8
  • 45
  • 71