Reputation: 90762
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?
Upvotes: 27
Views: 8981
Reputation: 2284
If you want to do work (that requires parameters) on the main-thread, you have to pass those parameters to the constructor.
Everything you pass via .execute() will be run on the async-thread.
As buptcoder mentioned, mainly that will concern UI stuff, but also disk/database operations can be thread-critical
Upvotes: 0
Reputation: 36449
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.
Upvotes: 12
Reputation: 2702
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.
Upvotes: 2