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

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

5 мая 2023 г.

Предыдущая статья из этой серии: Выполнение фоновых задач в Android с помощью WorkManager: часть 2

Последовательность задач

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

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

Это можно сделать с помощью трех последовательных задач:

  • скачать архив
  • распаковка архива
  • обработка файлов

Давайте посмотрим, как мы можем запустить эти задачи последовательно

Во-первых, давайте удостоверимся, что задачи, запущенные обычным образом, будут выполняться параллельно. Запускаем сразу три задачи:

val worker1: WorkRequest = OneTimeWorkRequestBuilder<Worker1>()
    .build()  

val worker2: WorkRequest = OneTimeWorkRequestBuilder<Worker2>()  
    .build()  

val worker3: WorkRequest = OneTimeWorkRequestBuilder<Worker3>()  
    .build()  

WorkManager  
    .getInstance(this)  
    .enqueue(listOf(worker1, worker2, worker3))

Смотрим журнал:

2023-04-18 21:07:05 18984-19015 Worker2  D  doWork: start
2023-04-18 21:07:05 18984-19016 Worker3  D  doWork: start
2023-04-18 21:07:05 18984-19014 Worker1  D  doWork: start

2023-04-18 21:07:06 18984-19014 Worker1  D  doWork: end
2023-04-18 21:07:06 18984-19016 Worker3  D  doWork: end
2023-04-18 21:07:06 18984-19015 Worker2  D  doWork: end

Задачи запускались в 21:07:05 и выполнялись параллельно в разных потоках, и каждый выполнялся в свое время.

Мы видели параллельное исполнение. Теперь выполним их последовательно. Мы передаем первую задачу в метод beginWith и тем самым создаем начало последовательности задач. Далее, вызывая метод then, мы добавляем в последовательность вторую и третью задачи. И с помощью метода enqueue мы отправляем эту последовательность на запуск.

Результат:

2023-04-18 21:28:59 20007-20036 Worker1  D  doWork: start
2023-04-18 21:29:00 20007-20036 Worker1  D  doWork: end
2023-04-18 21:29:00 20007-20044 Worker2  D  doWork: start
2023-04-18 21:29:01 20007-20044 Worker2  D  doWork: end
2023-04-18 21:29:01 20007-20045 Worker3  D  doWork: start
2023-04-18 21:29:02 20007-20045 Worker3  D  doWork: end

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

Как критерии повлияют на выполнение последовательности задач?

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

Давайте рассмотрим пример.

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

Если какая-либо задача в последовательности завершается со статусом FAILURE, вся цепочка будет остановлена.

Мы можем комбинировать последовательное и параллельное выполнение задач.

WorkManager  
    .getInstance(this)  
    .beginWith(listOf(worker1, worker2))  
    .then(listOf(worker3, worker4))  
    .then(worker5)  
    .enqueue()

Здесь мы формируем последовательность, но при этом указываем две задачи для первого (beginWith) и второго (first then) шага последовательности.

В результате задачи worker1 и worker2 будут выполняться первыми и выполняться параллельно. После этого будут выполняться worker3 и worker4, также параллельно друг другу. И после этого - worker5.

В логах это будет выглядеть так:

2023-04-18 21:41:31 20434-20464 Worker1  D  doWork: start
2023-04-18 21:41:31 20434-20465 Worker2  D  doWork: start
2023-04-18 21:41:32 20434-20464 Worker1  D  doWork: end
2023-04-18 21:41:32 20434-20465 Worker2  D  doWork: end
2023-04-18 21:41:32 20434-20476 Worker3  D  doWork: start
2023-04-18 21:41:32 20434-20464 Worker4  D  doWork: start
2023-04-18 21:41:33 20434-20476 Worker3  D  doWork: end
2023-04-18 21:41:33 20434-20464 Worker4  D  doWork: end
2023-04-18 21:41:33 20434-20465 Worker5  D  doWork: start
2023-04-18 21:41:34 20434-20465 Worker5  D  doWork: end

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

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

Вот как это делается:

val chain12: WorkContinuation = WorkManager  
    .getInstance(this)  
    .beginWith(worker1)  
    .then(worker2)  

val chain34: WorkContinuation = WorkManager  
    .getInstance(this)  
    .beginWith(worker3)  
    .then(worker4)  

WorkContinuation.combine(listOf(chain12, chain34))  
    .then(worker5)  
    .enqueue()

WorkContinuation – это последовательность задач. Мы создаем последовательность chain12, состоящую из первой и второй задач, и последовательность chain34, состоящую из третьей и четвертой задач. Чтобы эти последовательности выполнялись параллельно друг другу, мы передаем их методу combine. Затем мы передаем пятую задачу методу then, который запускается после завершения всех последовательностей из combine.

Результат:

2023-04-19 15:58:20  1511-1534  Worker1 D  doWork: start
2023-04-19 15:58:20  1511-1535  Worker3 D  doWork: start
2023-04-19 15:58:21  1511-1534  Worker1 D  doWork: end
2023-04-19 15:58:21  1511-1535  Worker3 D  doWork: end
2023-04-19 15:58:21  1511-1571  Worker2 D  doWork: start
2023-04-19 15:58:21  1511-1534  Worker4 D  doWork: start
2023-04-19 15:58:22  1511-1571  Worker2 D  doWork: end
2023-04-19 15:58:22  1511-1534  Worker4 D  doWork: end
2023-04-19 15:58:22  1511-1571  Worker5 D  doWork: start
2023-04-19 15:58:23  1511-1571  Worker5 D  doWork: end

Запускаются первая и третья задачи, т.е. последовательности начинают выполняться параллельно. Когда обе последовательности выполнены, начинается пятая задача.

Уникальная работа

Мы можем сделать последовательность задач уникальной. Для этого мы запускаем последовательность с помощью метода beginUniqueWork.

private fun work() {  

    val worker1 = OneTimeWorkRequestBuilder<Worker1>()  
        .build()   

    val worker3 = OneTimeWorkRequestBuilder<Worker3>()  
        .build()  

    val worker5 = OneTimeWorkRequestBuilder<Worker5>()  
        .build()  

    WorkManager.getInstance(this)  
        .beginUniqueWork("work123", ExistingWorkPolicy.REPLACE, worker1)  
        .then(worker3)  
        .then(worker5)  
        .enqueue()  

}

findViewById<AppCompatButton>(R.id.startWork).setOnClickListener {  
    Log.e(TAG, "enqueue REPLACE")  
    work()  
}

Укажите имя последовательности, режим и первую задачу последовательности.

Мы указали REPLACE в качестве режима. Это означает, что если последовательность с таким именем уже запущена, то при следующем запуске текущая последовательность будет остановлена ​​и запущена новая.

Я добавил журналирование в вызов метода enqueue, который запускает последовательность. Давайте посмотрим, что происходит в журналах.

2023-04-19 16:52:32 17809-17809 WorkManagerActivity3 E  enqueue REPLACE
2023-04-19 16:52:33 17809-17882 Worker1              D  doWork: start
2023-04-19 16:52:38 17809-17882 Worker1              D  doWork: end
2023-04-19 16:52:38 17809-17914 Worker3              D  doWork: start
2023-04-19 16:52:43 17809-17914 Worker3              D  doWork: end
2023-04-19 16:52:43 17809-17952 Worker5              D  doWork: start

2023-04-19 16:52:46 17809-17809 WorkManagerActivity3 E  enqueue REPLACE
2023-04-19 16:52:46 17809-17880 Worker5              D  onStopped
2023-04-19 16:52:46 17809-17882 Worker1              D  doWork: start
2023-04-19 16:52:48 17809-17952 Worker5              D  doWork: end
2023-04-19 16:52:51 17809-17882 Worker1              D  doWork: end
2023-04-19 16:52:51 17809-17914 Worker3              D  doWork: start
2023-04-19 16:52:56 17809-17914 Worker3              D  doWork: end
2023-04-19 16:52:56 17809-17952 Worker5              D  doWork: start
2023-04-19 16:53:01 17809-17952 Worker5              D  doWork: end

16:52:32 — это первый запуск последовательности. Задачи начинают выполняться одна за другой.

16:52:46 - пока работает MyWorker5, я создаю и запускаю такую ​​же последовательность с тем же именем - work123. Текущая последовательность останавливается и запускается новая.

Режим KEEP будет поддерживать текущую выполняемую последовательность. Новый будет проигнорирован.

Код:

WorkManager.getInstance(this)  
    .beginUniqueWork("work123", ExistingWorkPolicy.KEEP, worker1)  
    .then(worker3)  
    .then(worker5)  
    .enqueue()

Журналы:

2023-04-19 17:05:17 20030-20030 WorkManagerActivity3 E  enqueue KEEP
2023-04-19 17:05:18 20030-20207 Worker1              D  doWork: start
2023-04-19 17:05:23 20030-20207 Worker1              D  doWork: end
2023-04-19 17:05:23 20030-20227 Worker3              D  doWork: start
2023-04-19 17:05:28 20030-20227 Worker3              D  doWork: end
2023-04-19 17:05:28 20030-20272 Worker5              D  doWork: start
2023-04-19 17:05:29 20030-20030 WorkManagerActivity3 E  enqueue KEEP
2023-04-19 17:05:33 20030-20272 Worker5              D  doWork: end

17:05:29 - Я попытался запустить последовательность еще раз, но был проигнорирован, так как в работе уже есть последовательность с таким названием.

Режим APPEND запустит новую последовательность после выполнения текущей.

Код:

WorkManager.getInstance(this)  
    .beginUniqueWork("work123", ExistingWorkPolicy.APPEND, worker1)  
    .then(worker3)  
    .then(worker5)  
    .enqueue()

Журналы:

2023-04-19 17:10:28 21636-21636 WorkManagerActivity3 E  enqueue APPEND
2023-04-19 17:10:28 21636-21730 Worker1              D  doWork: start
2023-04-19 17:10:33 21636-21730 Worker1              D  doWork: end
2023-04-19 17:10:33 21636-21784 Worker3              D  doWork: start
2023-04-19 17:10:38 21636-21784 Worker3              D  doWork: end
2023-04-19 17:10:38 21636-21836 Worker5              D  doWork: start
2023-04-19 17:10:39 21636-21636 WorkManagerActivity3 E  enqueue APPEND
2023-04-19 17:10:43 21636-21836 Worker5              D  doWork: end
2023-04-19 17:10:43 21636-21730 Worker1              D  doWork: start
2023-04-19 17:10:48 21636-21730 Worker1              D  doWork: end
2023-04-19 17:10:48 21636-21784 Worker3              D  doWork: start
2023-04-19 17:10:53 21636-21784 Worker3              D  doWork: end
2023-04-19 17:10:53 21636-21836 Worker5              D  doWork: start
2023-04-19 17:10:58 21636-21836 Worker5              D  doWork: end

17:10:39 - текущая последовательность не прерывалась, а новая запускалась сразу после окончания текущей.

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

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


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE