Как организовать длительные (затратные по времени) действия на Android?

Например, мы находимся в SomeActivity, и в действии есть кнопка, которая вызывает перемещение файлов из одного каталога в другой (назовем это заданием).

На BlackBerry я бы:

  1. нажмите неотменяемое всплывающее окно (диалоговый экран) с надписью «Подождите...»
  2. запустить поток, который выполняет задание
  3. по завершении потока закрыть всплывающее окно

Такой подход на 99,99% может гарантировать, что мы остаемся на том же экране после завершения задачи, пользователь просто видит всплывающее окно и ждет завершения задачи. Вращение устройства или что-то еще не нарушает желаемый рабочий процесс.

На Android все меняется. Я знаю, что есть AsyncTask, который, вероятно, предназначен для решения моего дела. Есть даже хороший пример того, как его следует использовать. Но поскольку нет гарантии того, как долго будет жить экземпляр Activity, AsyncTask следует отменить в onSaveInstanceState (и перезапустить в onRestoreInstanceState). Это означает, что при использовании AsyncTask нет гарантии, что мы сможем полностью выполнить задание после запуска. В некоторых случаях при отправке HTTP-запроса на создание пользователя я бы не хотел столкнуться с проблемой «пользователь уже существует для этого входа в систему» ​​при повторном запуске AsyncTask. Это возможно, поскольку AsyncTask может быть прерван, когда запрос уже отправлен (и сервер фактически выполняет свою работу — создает нового пользователя), но AsyncTask отменяется до того, как мы получили ответ.

Есть ли какое-либо решение для Android, чтобы получить поведение, подобное BB, указанное выше?


person Vit Khudenko    schedule 10.02.2010    source источник


Ответы (3)


Но поскольку нет гарантии того, как долго будет жить экземпляр Activity, AsyncTask следует отменить в onSaveInstanceState (и перезапустить в onRestoreInstanceState).

Или пусть им будет управлять Service.

person CommonsWare    schedule 11.02.2010
comment
Я должен изучить этот компонент, потому что мне уже дважды советовали использовать его. - person Vit Khudenko; 11.02.2010
comment
Привет, КоммонсВэр. Есть ли в какой-либо из ваших книг подробное объяснение того, как долго выполняющиеся задания должны быть спроектированы/кодированы на Android? - person Vit Khudenko; 05.04.2010
comment
Это немного зависит от того, что вы определяете как длительные задания. Я рассказываю об услугах с AsyncTask в одной книге (commonsware.com/Android) — см. github.com/commonsguy/cw-android/tree/master/Service/ для рассматриваемый проект. Я рассказываю об использовании AlarmManager для предотвращения бесконечных служб в другой книге (commonsware.com/AdvAndroid) — см. github.com/commonsguy/cw-advandroid/tree/master/SystemServices/ для этого проекта. - person CommonsWare; 05.04.2010
comment
Только сейчас увидел ваш ответ (думал, что будет какое-то уведомление по электронной почте о новом комментарии, но его не было). - person Vit Khudenko; 08.04.2010
comment
Я ищу решение для правильной обработки длительных потоков заданий во всплывающем окне прогресса, предполагая, что пользователь вызвал его из Activity. Проблема заключается в том, как синхронизировать поток заданий с жизненным циклом Activity. Это был мой оригинальный пост о. - person Vit Khudenko; 08.04.2010
comment
Итак, ваш первый (сервис с AsyncTask в WeatherPlus) представляет интерес. Прохладный. Как я понял, служба будет выполнять свою AsyncTask до самого конца (doInBackground() AsyncTask не будет прервана, если ОС не убьет весь процесс приложения). Таким образом, ротация устройств не приведет к прерыванию AsyncTask. После завершения AsyncTask отправляется широковещательная рассылка Intent. Активность может обрабатывать трансляцию, только будучи активной (между onResume() и onPause()). - person Vit Khudenko; 08.04.2010
comment
Извините за возможно глупые вопросы: Что будет, если на момент отправки трансляции Активность уже была убита ОС (из-за нехватки оперативной памяти). Будет ли трансляция активировать Activity для обработки трансляции? - person Vit Khudenko; 08.04.2010
comment
Нет, трансляция будет просто проигнорирована. - person CommonsWare; 08.04.2010
comment
Спасибо за ответ, CommonsWare. Я ищу лучший класс для диспетчера AsyncTasks, вызываемого из моих действий. Это может быть одно из следующих: 1. подкласс приложения; 2. подкласс обслуживания; 3. мои собственные статические вещи (класс, экземпляр которого нельзя создать). Как по мне, проще реализовать 3-й вариант. Но вопрос в том, будет ли он более устойчив к смерти, чем Сервис или Приложение? Также интересно, что проживет дольше - Приложение или Сервис? - person Vit Khudenko; 08.04.2010

Если ваша активность хочет оставаться на экране, вы можете просто запустить поток следующим образом:

final File fromFile = ...;
final File toFile = ...;

new Thread() {
    @Override
    public void run() {
      // do something with fromFile, toFile
    }
}.start();

Таким образом, GUI-Thread готов выполнять другие функции, такие как отображение

  android.app.ProgressDialog

Кроме того, рассмотрите возможность сделать диалог неотменяемым с помощью

  ProgressDialog.setCancelable(false);

Таким образом, пользователь может выйти только с помощью клавиши HOME, о чем вы получите уведомление, когда

  Activity.onPause()

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

  PowerManager pm = (PowerManager) ivContext.getSystemService(Context.POWER_SERVICE);
  Wakelock wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MyApp");
  wakeLock.acquire();

  // ... copy stuff ...

  wakeLock.release();

Конечно, вам также придется отпустить wakeLock, когда пользователь уходит с помощью HOME-Key.

Наконец, если вы хотите вызвать GUI-Elements из своего фонового потока, это будет работать только в том случае, если поток является частью GUI-Event-Loop, как обычный поток, в котором вы работаете, при вызове on... -Методы. Для этого ваш фоновый поток должен будет выполнить обратный вызов GUI-потока через обработчик. Нравится:

  private Handler mHandler = new Handler() { 
      @Override
      public void handleMessage(Message msg) {
          Log.v(TAG, "Got Message "+msg.what); // prints: Got Message 77
      // ... do GUI actions ...
      }    
  };

  // ... in Thread ...

  int lvInfo = 77;
  mHandler.sendEmptyMessage(lvInfo);

Вы даже можете помещать объекты в сообщение следующим образом:

  Message txtMsg = Message.obtain();
  textMsg.obj = "Hello World";
  mHandler.sendMessage(lvTextMsg);
person rflexor    schedule 11.02.2010
comment
rflexor, спасибо за ответ. Специально для точки Wakelocks. - person Vit Khudenko; 11.02.2010

В мае 2010 г. Google выпустил новый сеанс ввода-вывода под названием Разработка клиентских приложений REST для Android, в котором объясняется, как добиться именно того, о чем я просил.

Выяснилось, что вопрос довольно сложный, поэтому простого и быстрого решения нет. Решение требует глубоких знаний платформы/API Android. Это цена, вызванная гибкостью жизненных циклов процесса/действия приложения.

Я чувствую некоторую странность в том, почему эта информация не была представлена ​​с самой первой версии Android. Похоже, Google знал, как писать приложения на 100 % без ошибок, и по какой-то маркетинговой причине не разделял этот подход. Только представьте, сколько глючных приложений было написано к маю 2010 года.

В любом случае, я рад, что теперь у меня есть что-то, что мы называем передовым подходом.

person Vit Khudenko    schedule 14.04.2011