Фоновая работа приложения android kotlin

Руководство по фоновой работе в Android. Часть 1

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

  1. Основной поток, осуществление фоновой работы с помощью AsyncTask, публикация результатов в основном потоке.
  2. Затруднения при использовании AsyncTask. Loaders как один из способов их избежать.
  3. Работа в фоне с помощью ThreadPools и EventBus.
  4. RxJava 2 как метод асинхронной работы.
  5. Корутины в Kotlin как метод асинхронной работы.

Основы UI

Первое, что следует понять, – почему мы вообще беспокоимся о работе в фоне в мобильных приложениях.

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

Каждый метод жизненного цикла каждого компонента вашего приложения, включая Activity, Service и BroadcastReceiver, исполняется на UI-потоке.

Человеческий глаз преобразовывает сменяющиеся изображения в плавное видео, если частота смены достигает 60 кадров в секунду (да, это магическое число берется отсюда), давая основному потоку только 16 мc для прорисовки всего экрана.

Продолжительность сетевого вызова может быть в тысячи раз больше.

Когда мы хотим загрузить что-либо из Интернета (прогноз погоды, пробки, сколько стоит ваша часть биткоина в данный момент), мы не должны делать это из главного потока. Более того, Android не позволит нам, выбросив NetworkOnMainThreadException.

Читайте также:  Php rest api slim

Семь лет назад, когда я разрабатывал свои первые приложения на Android, подход от Google был ограничен использованием AsyncTasks. Давайте посмотрим, как мы писали код для общения с сервером (псевдокод преимущественно):

public class LoadWeatherForecastTask extends AsyncTask  < public Forecast doInBackground(String. params) < HttpClient client = new HttpClient(); HttpGet request = new HttpRequest(params[0]); HttpResponse response = client.execute(request); return parse(response); >>

Метод doInBackground() гарантированно будет вызван не на основном потоке. Но на каком? Зависит от реализации. Вот как Android выбирает поток (это часть исходного кода класса AsyncTask):

@MainThread public final AsyncTask executeOnExecutor(Executor exec, Params. params) < . mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); //

Здесь можно увидеть, что выполнение зависит от параметра Executor. Посмотрим, откуда он берется:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); . private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; . @MainThread public final AsyncTask execute(Params. params)

Как здесь указано, по умолчанию executor ссылается на пул потоков размера 1. Это означает, что все AsyncTasks в вашем приложении запускаются последовательно. Это не всегда было верно, так как для версий ОС от DONUT до HONEYCOMB использовался пул размером от 2 до 4(в зависимости от количества ядер процессора). После HONEYCOMB AsyncTasks снова выполняются последовательно по умолчанию.

Итак, работа выполнена, байты закончили свое длинное путешествие с другого полушария. Нужно превратить их во что-то понятное и разместить на экране. К счастью, наша Activity тут как тут. Давайте поместим результат в одно из наших View.

public class LoadWeatherForecastTask extends AsyncTask  < public Forecast doInBackground(String. params) < HttpClient client = new HttpClient(); . Forecast forecast = parse(response); mTemperatureView.setText(forecast.getTemp()); >>

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Но мы не делали никаких сетевых обращений на основном потоке! Правильно, но мы попытались нарушить другой закон UI. Пользовательский интерфейс можно менять только из UI-потока. Это верно не только для Android, но и практически для любой системы, с которой вы столкнетесь. Причину этого хорошо объяснили в Java Concurrency In Practice. Вкратце – архитекторы хотели избежать сложной блокировки при изменениях из нескольких источников (пользовательский ввод, биндинг и другие изменения). Использование единственного потока решает эту проблему.

Да, но UI все равно нужно обновлять. У AsyncTask есть еще метод onPostExecute, который вызывается на UI-потоке:

public void onPostExecutre(Forecast forecast)

Как эта магия работает? Посмотрим в исходном коде AsyncTask:

private void finish(Result result) < if (isCancelled()) < onCancelled(result); >else < onPostExecute(result); >mStatus = Status.FINISHED; > private static class InternalHandler extends Handler < public InternalHandler(Looper looper) < super(looper); >@Override public void handleMessage(Message msg) < AsyncTaskResultresult = (AsyncTaskResult) msg.obj; switch (msg.what) < case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; >> >

AsyncTask использует Handler для вызова onPostExecute в UI, ровно как и метод postOnUiThread в компонентах Android.

Handler прячет всю внутреннюю кухню. Какую именно? Идея состоит в том, чтобы иметь бесконечный цикл проверки сообщений, приходящих на UI-поток, и обрабатывать их соответствующе. Велосипедов тут никто не изобретает, хотя без кручения педалей не обошлось.

Для Android это реализовано классом Looper, который передается в InternalHandler в приведенном выше коде. Суть класса Looper находится в методе loop:

Он просто опрашивает очередь входящих сообщений в бесконечном цикле и обрабатывает эти сообщения. Это означает, что на UI-потоке должен быть инициализированный экземпляр Looper. Можно получить доступ к нему с помощью статического метода:

public static Looper getMainLooper()

Кстати, вы только что узнали, как проверить, вызывается ли ваш код в UI-потоке:

if (Looper.myLooper() === Looper.getMainLooper()) < // we are on the main thread >

Если вы попытаетесь создать экземпляр Handler в методе doInBackground, то получите другое исключение. Оно сообщит о необходимости наличия Looper для потока. Теперь вы знаете, что это значит.

Надо заметить, что AsyncTask может быть создан только в UI-потоке по указанным выше причинам.
Вы можете подумать, что AsyncTask – это удобный способ выполнения фоновой работы, так как он скрывает сложность и требует немного усилий при использовании, но есть несколько проблем, которые приходится решать по пути:

  • Каждый раз нужно писать достаточно много кода для решения относительно простой задачи
  • AsyncTasks ничего не знают о жизненном цикле. При неправильном обращении лучшее, что вы получите — утечка памяти, в худшем – сбой
  • AsyncTask не поддерживает сохранение состояния прогресса и повторное использование результатов загрузки.

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

Источник

Фоновая работа приложения android kotlin

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

Как бороться с Android'ом и не позволять ему уничтожать важные фоновые процессы, расскажет Сергей Смирнов, Android-разработчик CleverPumpkin, который так овладел инструментом WorkManager, что смог в фоновом режиме написать эту статью.

How the WorkManager works in Android

WorkManager появился как инструмент для отложенной фоновой работы. Начиная с первого выпуска, он предоставлял несколько базовых API:

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

Изначально это был инструмент только для отложенной фоновой работы, но в последних версиях Workmanager появилось несколько новых функций. Так, теперь появилась возможность запросить немедленное выполнение задачи, если приложение находится на переднем плане, и быть при этом уверенным в завершении этой работы. Например, приложение может продолжать обрабатывать фото, загружая их на сервер, даже если пользователь уже делает что-то другое.

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

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

WorkManager имеет свою базу данных, что позволяет сохранять информацию и статус работы в случае сбоя процесса или даже перезагрузки устройства. Это дает возможность восстановить статус выполнения работы и продолжить его с того места, на котором остановились, независимо от обстоятельств.

То есть, теперь WorkManager - не только инструмент для выполнения отложенных фоновых задач, но и инструмент для любой задачи, которая обязательно должна быть выполнена и завершена. Google рекомендует WorkManager как лучшее решение для устойчивого выполнения каких-либо задач в global scope: если ваше приложение работает, то задача будет выполнена даже при смене ориентации/конфигурации и других остановок Activity.

Давайте рассмотрим, как WorkManager поможет нам с выполнением длительных задач, даже когда пользователь переводит их в фоновый режим.

Стоит напомнить о сервисах – программных компонентах. Существуют background сервис – он же сервис для выполнения фоновых задач; и foreground сервис, который при выполнении таких же фоновых задач показывает пользователю уведомление, информирующее о том, что приложение запущено.

Пока приложение на переднем плане, мы можем запускать любые сервисы для фоновых задач. Но когда приложение переводится в фон, оно получает от системы еще несколько минут на выполнение задач, после чего по воле Android отключается. У foreground сервиса меньше шансов на уничтожение, но система все равно попытается за счет фонового процесса освободить ресурсы для основного на этот момент приложения. В этой ситуации мы не можем надеяться на то, что наши задачи будут выполнены – и тем более возобновлены позже. Выполнение длительных задач в этой ситуации становится практически невозможным.

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

Чтобы сделать это возможным, WorkManager связывает жизненный цикл процесса, в котором выполняется работа, с жизненным циклом foreground service. WorkManager по-прежнему осведомлен об этой работе, но foreground service полностью владеет жизненным циклом выполнения.

Так как foreground service требует показ уведомления пользователю, разработчики добавили API для WorkManager. Также WorkManager предоставляет API-интерфейсы, чтобы пользователь мог остановить выполнение работы прямо из уведомления.

Источник

Оцените статью