Подтвердить что ты не робот

Должен ли я давать параметры конструктору или AsyncTask.execute(params)?

Я пытаюсь понять, почему Android AsyncTask предоставляет параметры через execute и почему передача затем в конструктор, похоже, не выполняется (в документах, по крайней мере).

Именно так мне кажется наиболее разумным (естественно, фактическая задача, с которой я работаю, - это больше, чем просто калькулятор суммы):

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);
    }
}

Это будет использоваться таким образом:

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

Однако это не так, как документация говорит об этом; он использует аргументы для execute(), как это (в крайнем случае не использует конструктор вообще, но все равно использует одно поле, потому что иначе это было бы слишком ужасно):

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);
    }
}

Выполнение этой задачи будет выглядеть примерно так:

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

Еще один вариант, который я рассмотрел, - это предоставить индикатор выполнения конструктору и данным для вызова execute, но я все еще не могу использовать onPreExecute, поскольку я не знаю максимальное значение. (Я бы предпочел использовать истинный макс, а не устанавливать максимальное значение произвольно и вычислять процент... просто кажется приятнее.)

Где баланс? Что мне делать? Что-то не так с использованием конструктора?

4b9b3361

Ответ 1

Что касается того, почему документы делают все в методе, это может быть результатом выбора их примера. Обычно вы, скорее всего, расширяете свою AsyncTask и используете только doInBackground(), а не конструктор.

В любом случае документация:

Наблюдение памяти


AsyncTask гарантирует, что все вызовы обратного вызова синхронизируются в таких способ, при котором следующие операции безопасны без явного синхронизаций.

• Задайте поля элемента в конструкторе или onPreExecute() и обратитесь к ним в > doInBackground (Params...).

• Установите поля-члены в doInBackground (Params...) и обратитесь к ним в onProgressUpdate (Прогресс...) и onPostExecute (Результат).

Это означает, что вы должны быть хорошо с обоими подходами.

И в качестве примечания я использовал AsyncTask с параметрическим конструктором без проблем, поэтому я могу создать резервную копию содержимого документации.

Кроме того, для вашего конкретного случая, если у параметра ProgressBar было задано другое максимальное значение, установленное заранее, оно должно быть равно 100. Это означает, что вы можете принять свой конструктор только в ProgressBar и иметь doInBackground() принять в данных (который также должен быть переменной-членом). Затем, обновляя прогресс, сделайте

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

Это не будет идеально, и вы можете преобразовать в double, если хотите повысить точность, но это должно сделать код более легким для понимания.

Ответ 2

На мой взгляд, если я хочу передать некоторые значения для первоначального пользовательского интерфейса, точно так же, как вы упомянули ProgressBar, мне нужно сделать это в OnPreExecute(). И поскольку OnPreExecute() не принимает никаких параметров, я предпочитаю поместить значения в конструктор в качестве параметров. Если некоторые значения не относятся к UI, просто нужно указать doInBackground, например, загружать URL-адреса файлов, я передам их как параметры выполнения.

Итак, в сводке, если значения около UI, передайте их в конструкторе, если значения только используются в doInBackground, передайте их как параметры выполнения.

Ответ 3

Если вы хотите выполнить работу (требующую параметров) в основном потоке, вам необходимо передать эти параметры в конструктор.

Все, что вы передаете через .execute(), будет запускаться в async-потоке.

Как упоминалось в buptcoder, в основном это касается элементов пользовательского интерфейса, но также операции с дисками/базами данных могут быть критическими для потоков