AsyncTask's get() method: Is there any scenario where it is actually the best option?
Asked Answered
C

9

20

After answering this question, I got a doubt about the sense/usefulness of using the get() method of Android's AsyncTask class.

public final Result get ()

Waits if necessary for the computation to complete, and then retrieves its result. 

Basically, a synchronous solution to the AsyncTask class, that blocks (freezes) the UI until the background operations are finished.

Other than test purposes, and even in those cases, I can't really think in any scenario where it is actually a good solution, but I may be wrong, so I feel curious.

If you need the user to actually wait until the AsyncTask finishes, you can show a Dialog or ProgressDialog, having the control of the UI in every moment. I know it's not exactly the same, but IMHO it's a much better approach than using the get() method.

Chancellor answered 4/6, 2013 at 8:11 Comment(6)
To me, the only use of get() is to escape NetworkOnMainThread exception, in another word it is useless. I do not see any reason why not just write a full normal function with return object if you need a value from get().Ternary
That's my point: it seems useless.Chancellor
Although the question itself is interesting, the "best" option is often very subjective. Can you define "best option"?Herbalist
While it's true that "best" option can be very subjective, a possible objective answer could be based in performance, for example. The real "spirit" of the question is if there's even a "good" option scenario, because it seems to me that there isn't, that there's always a better solution for the problem than using the get() method.Chancellor
it might be useful when you want to convert java-code to android that uses the Future class: docs.oracle.com/javase/6/docs/api/java/util/concurrent/… . So the things that used the Future stuff are now also executed in the AsyncTask threadpool. But you are right - i don't see much sense in it tooDissemble
@SimonMeyer, that's a good answer (I would upvote it if it was posted as an answer).Chancellor
T
18

AsyncTask isn't the only way of doing background operations. Indeed, the documentation says that AsyncTask should only be used for operations take at most a few seconds. So if you've got tasks that take longer, they should be coded via classes that implement the runnable interface. Such tasks in other (non AsyncTask) threads may well want to wait for an AsyncTask to finish, so it seems to me that the idea that there are no situations where one would want to use AsyncTask.get() is false.

Update: In response to a comment, to emphasize that this could be a valid use of AsyncTask.get(), the following is possible:

  • There could be AsyncTasks that get initiated from the UI thread, which might involve communicating over the internet, e.g. loading a web page, or communicating with a server. Whatever the results of the AsyncTask are, some (or all) of the results are needed to update the screen. Hence an AsyncTask with its doInBackground followed by onPostExecute on the UI thread makes sense.
  • Whenever the UI thread initiates an AsyncTask, it places the AsyncTask object in a queue, for additional processing by a separate background thread once the results are available.
  • For each AsyncTask in the queue in turn, the background thread uses AsyncTask.get() to wait for the task to finish, before doing the additional processing. One obvious example of additional processing could simply be logging all such AsyncTask activities to a server on the internet, so it makes sense to do this in the background.
The following code shows what I mean. The app calls KickOffAsynctask(...) whenever it wants to do an AsyncTask, and there's a background thread that will automatically pickup the task for post-processing once the task is complete.
public class MyActivity extends Activity {

    static class MyAsyncTaskParameters { }
    static class MyAsyncTaskResults { }

    Queue<MyAsyncTask> queue;   // task queue for post-processing of AsyncTasks in the background
    BackgroundThread b_thread;  // class related to the background thread that does the post-processing of AsyncTasks

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);     
        queue = new  ConcurrentLinkedQueue<MyAsyncTask>();
        b_thread = new BackgroundThread(queue);
        b_thread.Start();       
    }

    void KickOffAsynctask(MyAsyncTaskParameters params) {
        MyAsyncTask newtask = new MyAsyncTask();
        newtask.execute(params);
        synchronized(queue) {
            queue.add(newtask);
        }
    }

    static class MyAsyncTask extends AsyncTask<MyAsyncTaskParameters, Void, MyAsyncTaskResults> {

        @Override
        protected MyAsyncTaskResults doInBackground(MyAsyncTaskParameters... params) {
            MyAsyncTaskResults results = new MyAsyncTaskResults();
            // do AsyncTask in background
            return results;
        }

        @Override
        protected void onPostExecute(MyAsyncTaskResults res){
            // take required results from MyAsyncResults for use in the user interface
        }

    }

    static class BackgroundThread implements Runnable {
        // class that controls the post processing of AsyncTask results in background

        private Queue<MyAsyncTask> queue;
        private Thread thisthread;
        public boolean continue_running;

        public BackgroundThread(Queue<MyAsyncTask> queue) {
            this.queue=queue; thisthread = null; continue_running = true;
        }

        public void Start() {
            thisthread = new Thread(this);
            thisthread.start();
        }

        @Override
        public void run() {
            try {
                do {
                    MyAsyncTask task;
                    synchronized(queue) {
                        task = queue.poll();
                    }
                    if (task == null) {
                        Thread.sleep(100);
                    } else {
                        MyAsyncTaskResults results = task.get();
                        // post processing of AsyncTask results in background, e.g. log to a server somewhere
                    }
                } while (continue_running);     
            } catch(Throwable e) {
                e.printStackTrace();
            }           
        }

    }

}

Update2. Another possible valid use of AsyncTask.get() has occurred to me. The standard advice is not to use AsyncTask.get() from the UI thread, because it causes the user interface to freeze until the result is available. However, for an app where stealth is needed, that may be exactly what is required. So how about the following situation: James Bond breaks into the hotel room of Le Chiffre and only has a couple of minutes to extract all the data from the villian's phone and install the monitoring virus. He installs the app provided by Q and starts it running, but he hears someone coming so he has to hide. Le Chiffre enters the room and picks up his phone to make a call. For a few seconds the phone seems a bit unresponsive, but suddenly the phone wakes up and he makes his phone call without further thought. Of course, the reason for the unresponsiveness was the fact that Q's app was running. It had various tasks to do, and some of them needed to be done in a particular order. The app used two threads to do the work, namely the UI thread itself and the single background thread that processes AsyncTasks. The UI thread was in overall control of all the tasks, but because some tasks needed to be done before other tasks, there were points in the app where the UI thread used AsyncTask.get() while waiting for the background task to finish :-).

Travistravus answered 11/6, 2013 at 19:39 Comment(14)
I agree with what you say about only using AsyncTask for operations that take at most a few seconds. Indeed, a week ago I wrote a comment to @CFlex's answer specifying exactly what the official docummentation states about that.Chancellor
BTW, as the AsyncTask docummentation states, there are a few threading rules that must be followed for this class to work properly: - The AsyncTask class must be loaded on the UI thread. - The task instance must be created on the UI thread. - execute(Params...) must be invoked on the UI thread. So, if, as you say, one uses other (non AsyncTask) background threads to perform heavier tasks, then AsyncTask cannot be used inside those already background threads.So AsyncTask.get() isn't a good nor a possible idea in that case.Chancellor
Hi Alejandro, I've updated my answer in response to your comment. From the way that you phrased the question, presumably it's only necessary to find one sensible use of AsyncTask.get() to stand a chance of earning the bounty. Do you agree? (PS. was it you who downvoted my answer here?)Travistravus
Hi Paul (checked your profile to know your name ;). Thanks for your reply! I did downvote your answer, but don't take it as something personal. It's much simpler: I consider it was not only wrong but a bad answer, as it can't even be implemented. You've updated your answer and your effort deserves the downvote to be undone. I will reward the bounty to the best given answer (IMO, obviously). I won't leave it ungiven, although IMHO there haven't been a really good answer.Chancellor
OK thanks Alejandro :-). If I can think of any other sensible (and implementable) uses of AsyncTask.get() before the bounty deadline expires then I'll update my answer accordingly.Travistravus
IMHO, your answer has two big problems: 1.- You talk about launching in a background thread a queue of AsyncTasks. As I already said, the official docummentation says that this is not possible. 2.- Even if it was possible (for example, using runOnUIThread to get back to the foreground and execute each AsyncTask), using the get() method will block (freeze) the UI during the execution of the queue of AsyncTasks. Blocking UI leads to "Application not responsive dialog" which annoys users.Chancellor
This has a very simple alternative: forget the get() method and use the AsyncTask asynchronously with callbacks. The UI won't get blocked and you will get exactly the same result.Chancellor
So that's my point: there always seem to be a better solution than using the get()method.Chancellor
Just spotted your comments, but haven't yet had a chance to review because I was working on my update2. Need to go out now, I'll reply later. Quick response: my first thought is that you've misunderstood update1. If so, I'll try and edit later to make it clearer, or admit defeat!Travistravus
In fact, I posted my comments before your update 2! I'll take a look at it.Chancellor
Definitely, your update 2 deserves my upvote ;o) On the other hand, the fact that Q's app had various tasks to do, and some of them needed to be done in a particular order, doesn't mean he should use the get() method. In fact, Q can easily implement the AsyncTask with callbacks and avoid an UI freezed, so Le Chiffre won't even notice a single problem with his phone.Chancellor
I've added some code to illustrate my update1 case. As far as I can see, it's a valid use of AsyncTask.get() that won't block the user interface.Travistravus
Paul, in your Update1, your KickOffAsynctask isn't called anywhere, so the queue is empty and I imagine no AsyncTasks are being executed.Chancellor
Hi Alejandro, the code is very incomplete, and is just meant to illustrate the idea in terms of the AsyncTask, the app's own background thread, together with the synchronized queue object. As you say, KickOffAsynctask is never called, which means as it stands no AsyncTask will ever execute. One way to make it functional would be to create a user interface with a single button, so that when the button is pressed KickOffAsynctask is called. If you do that, then I'm sure you will see all the different bits being called when the button is pressed.Travistravus
M
5

The get method should never be used in the UI thread because it will (as you noticed) freeze your UI. It should be used in a background thread to wait for the end of your task, but generally speaking, try to avoid AsyncTask as much as possible (WHY?).

I would suggest a callback or eventbus as a replacement.

Mayamayakovski answered 4/6, 2013 at 8:19 Comment(3)
i think that for tiny tasks , it's ok to use the asyncTask . examples: listView items loading, database modifications, images loading (but you should also use cache) . now you also have the volley library to help you with most of the asynctasks uses.Acalia
As the official documentation states: AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.Chancellor
Could you please give an example of an event bus being used with asynctask/futuretask?Zimmerman
A
5

if you run multiple asyncTasks in parallel , you might want that some tasks would wait for the result of others.

however, i think you should avoid running too many asyncTasks in parallel, because it will slow down the device.

Acalia answered 4/6, 2013 at 8:39 Comment(3)
The official documentation states: 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. If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.Chancellor
The point to where I want to get is that I can't imagine one single scenario where using the get() method is the best option.Chancellor
True. Another scenario would be that other threads would want to wait for it to finish.Acalia
S
4

I usually use the handler class to do the trick (Loading media async). Like:

public void setRightSongAdele(Song current)
{
    RetriveCorrelateGenreSong rcgs = new RetriveCorrelateGenreSong(current);
    new Thread(rcgs).start();
}

@SuppressLint("HandlerLeak")
Handler updateRightAdeleHandler = new Handler()
{
    @Override
     public void handleMessage(Message msg) {
        songNextText.setText(utils.reduceStringLength(rightSong.getTitle(), 15));
        adeleNext.setImageBitmap(utils.getAlbumArtFromSong(rightSong.getPath(), getApplicationContext()));
    }
};
Spavined answered 4/6, 2013 at 8:47 Comment(1)
Thanks for your answer, but what I am really asking is if there's any scenario where it is actually the best option... I'm not asking for alternatives ;o)Chancellor
C
3

I post my own answer, as it is what I think at this moment and nobody has given it.

My curiosity is still alive, so I wont mark this one as correct. If someone answers this question brilliantly, I will mark his/her answer as correct.

My answer is that there is actually no scenario where using the AsyncTask's get() method is the best solution.

IMHO, using this method is pointless. What I mean is that there always seem to be a better solution.

Likewise, I would say it's semantically shocking, as calling it changes your AsyncTask into an effective "SyncTask".

Chancellor answered 4/6, 2013 at 21:13 Comment(0)
P
2

As the documentaion stands:

"Waits if necessary for the computation to complete, and then retrieves its result."

So it is mandatory that you avoid to call it on the UI Thread, otherwise you will get the NetworkOnMainThreadException. I would use it in a test enviroment which involves net operations or to wait another task to finish.

EDIT: I was wrong about NetworkOnMainThreadException.

Papal answered 6/6, 2013 at 9:16 Comment(5)
I can't test it right now, but I would say that calling it on the UI Thread doesn't throw the NetworkOnMainThreadException because it really operates in the background. BTW, I agree with you about using it just in test environment, although it still isn't the best option for those cases.Chancellor
@AlejandroColorado It will throw that exception. Test when you have a bit of time. Could you give me a feedback about?Papal
Not a good day for testing, but I'm pretty sure it wouldn't throw that exception. Well, if the AsyncTask is well coded (obviously, if some of the network tasks are done in the onPreExecute() or onPostExecute() instead of in the doInBackground, it should throw that exception).Chancellor
I promise I will test it when I can and give you the feedback.Chancellor
A promise is a promise: as I expected it didn't throw the NetworkOnMainThreadException.Chancellor
S
2

It appears as though AsyncTask.get() blocks the caller thread, where AsyncTask.execute() does not.

You might want to use AsyncTask.get() for test cases where you want to test a particular Web Service call, but you do not need it to be asynchronous and you would like to control how long it takes to complete. Or any time you would like to test against your web service in a test suite.

Syntax is the same as execute:

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
    }

new DownloadFilesTask().get(5000, TimeUnit.MILLISECONDS);
Sennet answered 13/6, 2013 at 5:19 Comment(1)
Thanks for your reply! According to this, this and this, your ProgressDialog won't be correctly shown.Chancellor
E
0

You can use it in your AutoCompleteTextView's adapter's filter.

private class Adapter extends BaseAdapter implements Filterable{
    getFilter(){
        return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            List<String> suggestions = MyApiSuggestionsApi(constraint).get();
            return packageAsFilterResults(suggestions);
        }
    }
}
Effusion answered 14/3, 2014 at 1:4 Comment(1)
Maybe I am missing something, but how is this related to the question? What it MyApiSuggestionsApi?Disorganize
R
-1

I have an application to edit photos, the user is capable of opening photos from URLs.

I used an AsyncTask to download the photo from the URL, during this time the UI should be blocked. what i did is I showed a ProgressBar starts the AsyncTask's get() method than hide the ProgressBar..

It seemed the best and simplest solution for me.

Restrain answered 13/6, 2013 at 6:53 Comment(3)
Thanks for your reply! You would get the same result showing the ProgressBar in the onPreExecute() and dismissing it in the onPostExecute() and the UI wouldn't get blocked. Blocking UI can lead to "Application not responsive dialog" which annoys users.Chancellor
Likewise, according to this, this and this, your ProgressDialog won't be correctly shown.Chancellor
Thanks @Alenjandro. the reason to block The UI is that I don't want the user to make modifications before the Image is completely downloaded.Restrain

© 2022 - 2024 — McMap. All rights reserved.