6. Многопоточность - kpb90/start_android GitHub Wiki

Handler.

В Android к thread может быть привязана очередь сообщений. В эту очередь мы можем складывать сообщения. Android будет за очередью следить и отправлять сообщения на обработку. Можно указать, чтобы сообщение ушло на обработку не сразу, а спустя определенное кол-во времени.

Handler - это механизм, который позволяет работать с очередью сообщений.

Handler привязан к конкретному thread и работает с его очередью.

Handler умеет помещать сообщения в очередь. При этом он ставит самого себя в качестве получателя этого сообщения. И когда приходит время, система достает сообщение из очереди и отправляет его адресату (т.е. в Handler) на обработку.

Handler дает нам две интересные и полезные возможности:

  1. реализовать отложенное по времени выполнение кода

  2. выполнение кода не в своем потоке

В этом уроке сделаем небольшое приложение. Оно будет эмулировать какое-либо долгое действие, например закачку файлов и в TextView выводить кол-во закачанных файлов. С помощью этого примера мы увидим, зачем может быть нужен Handler.

Основной поток приложения отвечает за экран, его нельзя грузить чем-то тяжелым, долгоиграющие задачи нужно переносить в отдельный поток.

Работа с view-компонентами доступна только из основного потока. Новые потоки - не имеют доступа к элементам экрана.

=> Проблема

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

Решение: использовать Handler

Схема работы:

  • создаем в основном потоке Handler
  • в долгоиграющем потоке обращаемся к Handler и с его помощью помещаем в очередь сообщение для него же самого
  • система берет это сообщение, видит, что адресат – Handler, и отправляет сообщение на обработку в Handler
  • Handler, получив сообщение, обновит TextView

Таким образом Handler выступит в качестве «моста» между потоками.

Handler. Посылаем простое сообщение

Сообщение может содержать в себе атрибуты. what - самый простой вариант атрибутов arg1 и arg2 - int obj - Object

obtainMessage (int what, int arg1, int arg2) - метод для создания сообщения sendEmptyMessage(int what) - отправка сообщения sendMessage(Message msg) - отправка сообщения **handleMessage ** - метод отвечает за обработку сообщений, которые предназначены для этого Handler public void handleMessage(android.os.Message msg) {

    switch (msg.what) {

      // Работа с UI

    }

}

AsyncTask

Основы

Чтобы работать с AsyncTask, надо создать класс-наследник и в нем прописать свою реализацию необходимых нам методов.Рассмотрим некоторые методы:

  • doInBackground – будет выполнен в новом потоке, здесь решаем все свои тяжелые задачи. Т.к. поток не основной - не имеет доступа к UI.

  • onPreExecute – выполняется перед doInBackground, имеет доступ к UI

  • onPostExecute – выполняется после doInBackground (не срабатывает в случае, если AsyncTask был отменен - об этом в следующих уроках), имеет доступ к UI

Официальный хелп дает 4 правила использования AsyncTask:

    • объект AsyncTask должен быть создан в UI-потоке
    • метод execute должен быть вызван в UI-потоке
    • не вызывайте напрямую методы onPreExecute, doInBackground, onPostExecute и onProgressUpdate
    • AsyncTask может быть запущен (execute) только один раз, иначе будет exception

Параметры. Промежуточные результаты

При описании класса-наследника AsyncTask мы в угловых скобках указываем три типа данных:

  1. Тип входных данных. Это данные, которые пойдут на вход AsyncTask

  2. Тип промежуточных данных. Данные, которые используются для вывода промежуточных результатов

  3. Тип возвращаемых данных. То, что вернет AsyncTask после работы.

  • execute – этот метод мы явно вызываем, чтобы начать выполнение задачи. В него мы передаем набор данных определенного типа. Этот тип указан первым в угловых скобках при описании AsyncTask (в нашем примере это String).

  • publishProgress – явно вызываем в методах doInBackground, onPreExecute или onPostExecute. На вход передаем промежуточные данные определенного типа. Этот тип указан вторым в угловых скобках при описании AsyncTask (в нашем примере это Integer).

  • onProgressUpdate – метод получает на вход промежуточные результаты. Сами не вызываем, вместо этого используем метод publishProgress. То, что передаем в publishProgress, попадает в onProgressUpdate.

Итоговый результат. Метод get

Используем третий параметр AsyncTask. Это тип (класс) объекта, который должен нам вернуться из AsyncTask. Получить этот объект мы можем двумя способами:

  1. Он передается на вход метода onPostExecute, который срабатывает по окончании задачи

  2. Метод get возвращает нам этот объект (ждет пока не выполнится задача. Так же есть реализация get с timeout)

Cancel – отменяем задачу в процессе выполнения

Для отмены задачи рекомендуется в doInBackground периодически вызывать метод isCancelled. Как только мы выполним метод cancel для AsyncTask, isCancelled будет возвращать true. А это значит, что мы должны завершить метод doInBackground.

Т.е. метод cancel – это мы ставим метку, что задачу надо отменить. Метод isCancelled – мы же сами эту метку читаем и предпринимаем действия, для завершения работы задачи.

Метод cancel возвращает boolean. Мы получим false, если задача уже завершена или отменена.

Status – статусы задачи

  • PENDING – задача еще не запущена

  • RUNNING – задача в работе

  • FINISHED – метод onPostExecute отработал, т.е. задача успешно завершена

Status – Поворот экрана