Лучшая практика для повторяющихся сетевых задач?

У меня есть небольшое приложение для Android, в котором мне нужно делать некоторые действия по FTP каждые пару секунд. Узнав на собственном горьком опыте, что запуск сетевых материалов в потоке пользовательского интерфейса - это то, что Android не очень любит, я пришел к следующему решению:

// This class gets declared inside my Activity
private class CheckFtpTask extends AsyncTask<Void, Void, Void> {
    protected Void doInBackground(Void... dummy) {
        Thread.currentThread().setName("CheckFtpTask");
        // Here I'll do the FTP stuff       
        ftpStuff();
        return null;
    }
}

// Member variables inside my activity
private Handler checkFtpHandler;
private Runnable checkFtpRunnable;

// I set up the task later in some of my Activitiy's method:
checkFtpHandler = new Handler();
checkFtpRunnable = new Runnable() {
    @Override
    public void run() {
        new CheckFtpTask().execute((Void[])null);
        checkFtpHandler.postDelayed(checkFtpRunnable, 5000);
    }
};
checkFtpRunnable.run();

Является ли хорошей практикой выполнение повторяющейся задачи, которая не может выполняться напрямую в потоке пользовательского интерфейса? Кроме того, вместо постоянного создания нового объекта AsyncTask вызовом

new CheckFtpTask().execute((Void[])null);

можно ли создать объект CheckFtpTask один раз, а затем использовать его повторно? Или это даст мне побочные эффекты?

Заранее спасибо, Йенс.


person Jens    schedule 13.09.2013    source источник


Ответы (3)


можно ли один раз создать объект CheckFtpTask, а затем использовать его повторно? Или это даст мне побочные эффекты?

Нет, будут побочные эффекты. Цитирование документации Правила создания потоков:

Задача может быть выполнена только один раз (при повторной попытке выполнения будет выдано исключение).

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

И я не уверен, зачем вам нужны Runnable или Handler. AsyncTask имеет методы, которые работают на UI Thread (на самом деле все, кроме doInBackground()), если вам нужно обновить UI.

Проверьте этот ответ, если вам нужно обратный вызов для обновления UI после завершения задачи.

person codeMagic    schedule 13.09.2013
comment
Зачем мне Runnable и Handler? Хороший вопрос. Я подумал, что это хороший способ сделать задачу повторяющейся с помощью postDelayed. Было бы лучше вызвать Thread.sleep() внутри цикла в doInBackground()? Во всяком случае, пока я пишу это, я обнаружил ошибку: Не имеет смысла вызывать postDelayed() в Runnable. Если задача занимает более 5 секунд, новая будет создана до того, как завершится старая. Это не то, чего я хочу, поэтому я переместил checkFtpHandler.postDelayed(checkFtpRunnable, 5000); в onPostExecute()из AsyncTask. - person Jens; 14.09.2013
comment
Вся задача (как и сейчас!) ничего не делает в пользовательском интерфейсе. Я просто выбрал комбинацию AsyncTask плюс Runnable/Handler, потому что мне нужна повторяющаяся задача, которая не находится в потоке пользовательского интерфейса с (позже) опцией для выполнения некоторого пользовательского интерфейса. вещи. Это звучит разумно? - person Jens; 14.09.2013
comment
Вы можете вызвать Thread.sleep() в doInBackground(), скажем, в for loop или while loop, чтобы он повторялся. - person codeMagic; 14.09.2013
comment
Но когда у меня есть цикл inifinte с Thread.sleep() в нем, я теряю возможность делать вещи пользовательского интерфейса, верно? - person Jens; 14.09.2013
comment
Нет, совсем нет. Вы можете обновить UI любым другим методом AsyncTask, например onProgressUpdate() или onPostExecute(). doInBackground() — единственный метод AsyncTask, который не работает на UI - person codeMagic; 14.09.2013
comment
Думаю, я этого не понимаю... При работе в бесконечном цикле ни onPostExecute(), ни onProgressUpdate() не вызываются. Итак, как я могу обновить пользовательский интерфейс? - person Jens; 14.09.2013
comment
Какую часть вы не получите, и я постараюсь помочь? - person codeMagic; 14.09.2013
comment
onPostExecute() не будет, пока вы не вернете значение из doInBackground(). Но onProgressUpdate() будет звонить publishProgress() из doInBackground() в любое время. - person codeMagic; 14.09.2013
comment
Вы сказали, что я могу обновить пользовательский интерфейс другими способами. Но когда у меня есть бесконечный цикл в doInBackground(), другие методы (onProgressUpdate() и onPostExecute()) никогда не вызываются, не так ли? Или onProgressUpdate() в моем примере просто не вызывается, потому что я создал класс как AsyncTask<Void, *Void*, Void>? - person Jens; 14.09.2013
comment
Правильно, вы должны указать второму param тип данных, который будет принимать onProgressUpdate(), и он будет вызываться только при вызове publishProgress(). - person codeMagic; 14.09.2013
comment
Получил это сейчас. По какой-то странной причине я думал, что onProgressUpdate() должен вызываться автоматически, но, конечно, для этого вам нужно вызвать publishProgress(). :-) - person Jens; 14.09.2013
comment
Итак, исходя из ресурсоэффективного подхода, вы бы сказали, что использование только AsyncTask в бесконечном цикле (с Thread.sleep()) лучше, чем моя первая версия с Handler/Runnable? - person Jens; 14.09.2013
comment
Я не знаю о превосходном, но с AsyncTask остальные обычно не нужны или не нужны - person codeMagic; 14.09.2013

Вы должны создавать новую асинхронную задачу для каждого вызова.

См. документацию по Android: AsyncTask. Согласно этой документации:

Задача может быть выполнена только один раз (при повторной попытке выполнения будет выдано исключение).

В частности, ознакомьтесь с разделом правила создания потоков. Здесь есть аналогичный ответ, https://stackoverflow.com/a/18547396/2728623

person pfairbairn    schedule 13.09.2013

Java ThreadPools и ExecutorFramework позволяют выполнять потоки по мере необходимости и снижают накладные расходы на создание потоков. Проверьте singleThreadExecutor. Использование пула потоков также довольно просто!

person anguyen    schedule 14.09.2013