ОАО «Российская Авиакомпания ЭРЛЕН»

Russian Airlines Company "Airlen"

Ваш внешний IP: 3.239.76.25

Запуск фоновых задач в Android с помощью WorkManager: часть 1

Важной частью задач приложения является фоновая работа. Его можно скачать или загрузить, сжать или распаковать, синхронизировать и т. д. Когда-то сервисы были предназначены для фоновой работы. Но в Android 8 они были сильно ограничены: если приложение не активно, то через какое-то время служба будет остановлена. И задолго до Android 8 разработчики начали использовать такие инструменты, как JobScheduler или Firebase JobDispatcher, для выполнения фоновых задач.

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

Это на самом деле очень легко использовать.

Создадим и запустим фоновую задачу

  1. Добавьте в зависимости:
// (for Java only)    
implementation("androidx.work:work-runtime:$work_version")

или

// for Kotlin + coroutines    
implementation("androidx.work:work-runtime-ktx:$work_version")
  1. Создайте класс, который наследует Workerкласс:
class MyWorker(context: Context, parameters: WorkerParameters) : Worker(context, parameters) {  

    private val TAG = this.javaClass.simpleName  

    override fun doWork(): Result {  
        Log.d(TAG, "doWork: start")  

        try {  

            TimeUnit.SECONDS.sleep(10)  

        } catch (e: InterruptedException) {  
            e.printStackTrace()  
        }  

        Log.d(TAG, "doWork: end")  

        return Result.success()  
    }  
}

В doWorkметоде нам предлагается разместить код, который будет выполняться. Здесь я просто делаю паузу на 10 секунд и возвращаюсь Result.success, что означает, что все прошло успешно. Нам не нужно возиться с потоками, потому что код будет выполняться в потоке, отличном от пользовательского интерфейса.

  1. Теперь нам нужно обернуть MyWorkerв WorkRequest:
val workRequest: WorkRequest = OneTimeWorkRequestBuilder<MyWorker>().build()

WorkRequestпозволяет задать условия запуска и входные параметры задачи. Пока ничего не ставим, а просто создаем OneTimeWorkRequestBuilder, на что говорим, что MyWorkerзадачу нужно будет запустить.

OneTimeWorkRequestBuilderимеет такое имя не просто так. Эта задача будет выполнена один раз. Есть еще PeriodicWorkRequest, но об этом позже.

  1. Теперь вы можете запустить задачу:
WorkManager  
    .getInstance(this)  
    .enqueue(workRequest)

Берем WorkManagerи переходим WorkRequestк его методу постановки в очередь. После этого задание будет запущено.

Смотрим лог:

2023-04-02 10:01:58.364 20001-20040 MyWorker D  doWork: start
2023-04-02 10:02:08.367 20001-20040 MyWorker D  doWork: end

Видно, что задача выполнялась 10 секунд, а код не выполнялся в UI-потоке.

Статус задачи

WorkManager предоставляет возможность отслеживать статус задачи. Например, в Activity мы пишем:

WorkManager.getInstance(this)  
    .getWorkInfoByIdLiveData(workRequest.id)  
    .observe(this) { workInfo ->  
        Log.d(TAG, "onChanged: " + workInfo.state)  
    }

Метод getWorkInfoByIdLiveData должен передавать идентификатор задачи, который может быть получен

WorkRequest.idметод. В качестве состояния мы получаем LiveData, подписываемся на него, и все изменения статуса нашей задачи будут приходить в onChangedметод. С помощью метода WorkInfo.state мы получим текущее состояние.

Мы запускаем:

2023-04-02 10:41:26.492 25791-25791 MainActivity D  onChanged: ENQUEUED
2023-04-02 10:41:26.530 25791-25826 MyWorker     D  doWork: start
2023-04-02 10:41:26.531 25791-25791 MainActivity D  onChanged: RUNNING
2023-04-02 10:41:36.531 25791-25826 MyWorker     D  doWork: end
2023-04-02 10:41:36.560 25791-25791 MainActivity D  onChanged: SUCCEEDED

Сразу после вызова метода enqueue задача находится в статусе ENQUEUED. Затем WorkManager определяет, что задачу можно запустить, и выполняет наш код. В этот момент статус меняется на РАБОТАЕТ. После выполнения статус будет SUCCEEDED, потому что такой статус мы вернули в doWorkметоде. Статус приходит к нам в поток пользовательского интерфейса.

Теперь снова запустим задачу и закроем приложение:

2023-04-02 10:50:38.057 25791-25791 MainActivity  D  onChanged: ENQUEUED
2023-04-02 10:50:38.067 25791-25923 MyWorker      D  doWork: start
2023-04-02 10:50:38.071 25791-25791 MainActivity  D  onChanged: RUNNING
2023-04-02 10:50:48.073 25791-25923 MyWorker      D  doWork: end

Обратите внимание, что задача выполнена, но статус SUCCEEDED не пришел. Почему? Потому что, закрыв Activity, мы просто отписались от LiveData, которая передала нам статус задачи. Но сама задача никуда не делась. Он никак не зависит от приложения и будет выполняться, даже если приложение закрыто.

Состояние

В нашей задаче мы вернули статус WorkInfo.SUCCESS, тем самым сообщив, что все ок. Есть еще два варианта:

FAILURE — в этом случае после выполнения задачи workInfo.state вернет FAILED. Для нас это сигнал о том, что задача не выполнена.

CANCELED — используется для обозначения того, что задача была отменена и не будет выполняться. Все зависимые задания также будут помечены как ОТМЕНЕННЫЕ и не будут выполняться.

ЗАБЛОКИРОВАНО — задача в настоящее время заблокирована, так как ее предварительные условия не были успешно выполнены.

Отменить задачу

Мы можем отменить задачу с помощью cancelWorkByIdметода, передав идентификатор задачи:

WorkManager.getInstance(this).cancelWorkById(workRequest.id)

Это вызовет onStoppedметод в MyWorkerклассе (если вы его реализовали). Кроме того, в MyWorkerклассе мы всегда можем использовать логический isStoppedметод, чтобы проверить, была ли задача отменена.

Если мы отследим статус задачи, то WorkInfo.state вернет Cancelled. Существует также cancelAllWorkметод, который отменит все ваши задачи. Но справка предупреждает, что использовать его крайне нежелательно; он может зацепить библиотеки, которые вы используете.

Ярлык

Вы можете присвоить тег задаче, используя addTagметод:

val workRequest: WorkRequest = OneTimeWorkRequestBuilder<MyWorker>()  
    .addTag("myWorkTag")  
    .build()

К одной задаче можно добавить несколько тегов. В WorkInfo есть getTagsметод, который вернет все теги, назначенные этой задаче.

Присвоив один тег нескольким задачам, мы можем отменить их все с помощью cancelAllWorkByTagметода:

WorkManager.getInstance(this).cancelAllWorkByTag("myWorkTag")

setInitialDelay

Выполнение задачи можно отложить на определенное время.

В методе setInitialDelay мы указали, что задача должна запускаться через 10 секунд после ее передачи в WorkManager.enqueue.

Периодическая задача

Мы OneTimeWorkRequestBuilderрассмотрели это одноразовая задача. И если вам нужно несколько исполнений через определенный промежуток времени, вы можете использовать PeriodicWorkRequestBuilder:

val periodicWorkRequest: WorkRequest = PeriodicWorkRequestBuilder<MyWorker>(30, TimeUnit.MINUTES)  
    .build()

В билдере установите интервал 30 минут. Теперь задача будет выполняться с этим интервалом.

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

WorkManager гарантирует, что задача будет запущена один раз в течение указанного интервала. И это может произойти в любой момент интервала — через 1 минуту, через 10 или через 29. С помощью параметра flex можно ограничить допустимый диапазон времени запуска.

val periodicWorkRequest: WorkRequest = PeriodicWorkRequestBuilder<MyWorker>(30, TimeUnit.MINUTES, 25, TimeUnit.MINUTES)  
    .build()

Помимо интервала в 30 минут дополнительно передаем флекс-билдеру параметр 25 минут. Теперь задание не будет запускаться в любой момент 30-минутного интервала, а только после 25-й минуты. Те, между 25 и 30 минут.

Азамат ХакерНун фото профиля
автор Азамат azamatnurkhojayev .Я Android-разработчик. Преподает курсы в школе программирования, пишет статьи о разработке.
Февраль 2024
Пн Вт Ср Чт Пт Сб Вс
 1234
567891011
12131415161718
19202122232425
26272829  

Сервер: 29.02.2024 08:02