Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I give params to the constructor or to AsyncTask.execute(params)?

I'm trying to figure out why Android's AsyncTask gives parameters through execute and why passing then to the constructor doesn't seem to be done (in the docs, at least).

This is the way that seems to me to make the most sense (naturally, the actual task I'm working with is more than just a sum calculator):

public class FooTask extends AsyncTask<Void, Integer, Long> {     private ProgressBar progressBar;     private int[] data;      public FooTask(ProgressBar progressBar, int... data) {         this.progressBar = progressBar;         this.data = data;     }      protected void onPreExecute() {         progressBar.setMax(data.length);         progressBar.setProgress(0);     }      protected Long doInBackground(Void... _) {         long sum = 0;         for (int i = 0; i < data.length; i++) {             sum += data[i];             publishProgress(i);         }         return sum;     }      protected void onProgressUpdate(Integer... progress) {         progressBar.setProgress(progress[0]);     }      protected void onPostExecute(Long result) {         Log.i(TAG, "Sum: " + result);     } } 

This would be used thus:

new FooTask(progressBar, 1, 2, 3).execute(); 

However, this isn't the way the documentation talks about doing it; it uses arguments to execute(), like this (taken to the extreme of not using a constructor at all, but still using one field because otherwise it'd be too horrible):

public class FooTask extends AsyncTask<Object, Integer, Long> {     private ProgressBar progressBar;     private boolean isMaxSettingUpdate = true;      protected Long doInBackground(Object... params) {         progressBar = params[0];         long sum = 0;         for (int i = 1; i < data.length; i++) {             sum += (int) data[i];             publishProgress(i - 1, data.length);         }         return sum;     }      protected void onProgressUpdate(Integer... progress) {         progressBar.setMax(progress[1]);         progressBar.setProgress(progress[0]);     }      protected void onPostExecute(Long result) {         Log.i(TAG, "Sum: " + result);     } } 

Execution of this task would look more like this:

new FooTask().execute(progressBar, 1, 2, 3); 

One other option I considered was giving the progress bar to the constructor and the data to the execute call, but then I still can't use onPreExecute as I don't know the max value. (I would prefer to use a true max rather than setting the max value arbitrarily and calculating the percentage... just seems nicer.)

Where's the balance? What should I do? Is there anything wrong with using the constructor?

like image 210
Chris Morgan Avatar asked May 27 '13 01:05

Chris Morgan


People also ask

What method must be overridden for using AsyncTask?

Usage. AsyncTask must be subclassed to be used. The subclass will override at least one method ( doInBackground(Params...) ), and most often will override a second one ( onPostExecute(Result) .)

Which method of the AsyncTask class can be used to achieve the same?

Methods of AsyncTask we can directly comminicate background operation using on doInBackground() but for the best practice, we should call all asyncTask methods . doInBackground(Params) − In this method we have to do background operation on background thread.

What are the problems in AsyncTask?

In summary, the three most common issues with AsyncTask are: Memory leaks. Cancellation of background work. Computational cost.


2 Answers

As to why the docs do everything in the method can be as a result of their example choice. Usually, you're more likely to extend your AsyncTask and only use doInBackground(), not the constructor.

Anyways, the documentation states:

Memory observability


AsyncTask guarantees that all callback calls are synchronized in such a way that the following operations are safe without explicit synchronizations.

•Set member fields in the constructor or onPreExecute(), and refer to them in > doInBackground(Params...).

•Set member fields in doInBackground(Params...), and refer to them in onProgressUpdate(Progress...) and onPostExecute(Result).

This means that you should be good with both approaches.

And as a side note, I have used an AsyncTask with a param-ed constructor without issues, so I can back up what the documentation states.

Also, for your specific case, unless the ProgressBar has had a different max value set beforehand, it should default to 100. This means that you can have your constructor accept in just the ProgressBar and have doInBackground() accept in the data (which should also be a member variable). Then when updating the progress, do

(progress[0]/data.length) * 100 

It won't be perfect, and you can convert to double if you want to increase accuracy, but this should make the code easiest to understand.

like image 181
A--C Avatar answered Sep 19 '22 23:09

A--C


In my opinion, if I want to pass some values to initial the UI, just like ProgressBar you mentioned, I need do it in OnPreExecute(). And because the OnPreExecute() don't accept any parameters, I prefer to put values in constructor as a parameters. If some values are not relative to UI, just need in doInBackground, for example, the download urls of files, I will pass them as parameters of execute.

So, in a summary, if the values are about UI, pass them in constructor, if values are just used in doInBackground, pass them as parameters of execute.

like image 26
buptcoder Avatar answered Sep 20 '22 23:09

buptcoder